home *** CD-ROM | disk | FTP | other *** search
/ PC World Komputer 2010 April / PCWorld0410.iso / pluginy Firefox / 1122 / 1122.xpi / chrome / tabmixplus.jar / content / tabmixplus / session / session.js < prev    next >
Text File  |  2009-10-11  |  160KB  |  3,470 lines

  1. // we call initDATASource also from pref-tabmix.js
  2. // don't move this to another file
  3. if (!("gIOService" in window)) {
  4.   __defineGetter__("gIOService", function() {
  5.     delete this.gIOService;
  6.     return this.gIOService = Components.classes["@mozilla.org/network/io-service;1"].
  7.                                getService(Components.interfaces.nsIIOService);
  8.   });
  9. }
  10.  
  11. const gRDFRoot = "rdf://tabmix";
  12. const HSitems = 3; // in history we have title, url, scrollpos
  13. var NC_TM = [];
  14. var gSessionPath = ["", "", "", ""];
  15. var nsIPrefServiceObj = Components.classes["@mozilla.org/preferences-service;1"].getService(Components.interfaces.nsIPrefService);
  16. var SessionPref = nsIPrefServiceObj.getBranch("extensions.tabmix.sessions.");
  17. var gThisWin = null;
  18. var gThisWinTabs = null;
  19. var gThisWinClosedtabs = null;
  20.  
  21. var tmSHEntry     = Components.interfaces.nsISHEntry;
  22. var tmSHistory    = Components.interfaces.nsISHistory;
  23. var tmRDFResource = Components.interfaces.nsIRDFResource;
  24.  
  25. const BUTTON_OK = 0;
  26. const BUTTON_CANCEL = 1;
  27. const BUTTON_EXTRA1 = 2;
  28. const SHOW_MENULIST = 1;
  29. const SHOW_TEXTBOX = 0;
  30. const HIDE_MENUANDTEXT = 2;
  31. const CHECKBOX_UNCHECKED = 0;
  32. const CHECKBOX_CHECKED = 1;
  33. const HIDE_CHECKBOX = 2;
  34. const SELECT_DEFAULT = 0;
  35. const SELECT_LASTSESSION = 1;
  36. const SELECT_CRASH = 2;
  37. const SHOW_CLOSED_WINDOW_LIST = 3;
  38. const DLG_SAVE = 0;
  39. const DLG_RENAME = 1;
  40. const NO_NEED_TO_REPLACE = -1;
  41.  
  42. function tmLog(aMessage, aShowCaller) {
  43.    var tmConsoleService = Components.classes['@mozilla.org/consoleservice;1']
  44.       .getService(Components.interfaces.nsIConsoleService);
  45.    try {
  46.       var caller = arguments.callee.caller;
  47.       var callerName = caller.name;
  48.       var callerCallerName = "";
  49.       if (aShowCaller) {
  50.         var prevCaller = caller.caller;
  51.         var prevCalerName = prevCaller && "name" in prevCaller ? prevCaller.name : prevCaller || "?" ;
  52.         callerCallerName = aShowCaller ? " (caller was " + prevCalerName + ")" : "";
  53.       }
  54.    } catch (e) {tmLog(e); callerCallerName = callerName ="";}
  55.    tmConsoleService.logStringMessage("TabMix " + callerName + callerCallerName + ":\n" + aMessage );
  56. }
  57.  
  58. //TMP_objectProperties
  59. function object2String(_obj, aMessage, aDisallowLog) {
  60.    aMessage = aMessage ? aMessage : "";
  61.    var _objS = _obj ? _obj.toString() : "_obj is " + typeof(_obj);
  62.    _objS +=  ":\n"
  63.  
  64.    try {
  65.    for (var item in _obj) {
  66.         var _val = _obj[item];
  67.          _objS += item + "[" + typeof(_val) + "]" + " =  " + _val + "\n";
  68.          if (typeof(_val) == "object") {
  69.             _objS += "\n";
  70.          }
  71.    }
  72.    } catch (er) { _objS += item + " =  " + "error in this item" + "\n";}
  73.    if (aDisallowLog)
  74.       _objS = aMessage + "\n======================\n" + _objS
  75.    else
  76.       tmLog(aMessage + "\n=============== Object Properties ===============\n" + _objS);
  77.    return _objS;
  78. }
  79.  
  80. function TMP_ASSERT(aError, aMsg) {
  81.   var caller = arguments.callee.caller;
  82.   var callerName = caller.name;
  83.   var errAt = callerName ? " at " + callerName : ""
  84.   var assertionText = "Tabmix Plus ERROR" + errAt + ":\n" + (aMsg ? aMsg + "\n" : "") + aError + "\n";
  85.   var stackText = "stack" in aError ? "Stack Trace: \n" + aError.stack : "";
  86.   var csv = Components.classes['@mozilla.org/consoleservice;1']
  87.       .getService(Components.interfaces.nsIConsoleService);
  88.   csv.logStringMessage(assertionText + stackText);
  89. }
  90.  
  91. function getObject(aObj, aMethod) {
  92.   aObj = aObj[aMethod.shift()];
  93.   if (aMethod.length)
  94.     aObj = getObject(aObj, aMethod);
  95.   return aObj;
  96. }
  97.  
  98. function showMethod(aMethod, aDelay) {
  99.   try {
  100.     if (typeof(aDelay) == "undefined")
  101.       aDelay = 500;
  102.  
  103.     var methodsList = aMethod.split(".");
  104.     if (methodsList[0] == "window")
  105.       methodsList.shift();
  106.     if (aDelay >= 0)
  107.       window.setTimeout(function () { tmLog(aMethod + " " + getObject(window || getTopWin(), methodsList).toString()); }, aDelay);
  108.     else
  109.       tmLog(aMethod + " " + getObject(window, methodsList).toString());
  110.   } catch (ex) {TMP_ASSERT(ex, "Error we can't show " + aMethod + " in showMethod");}
  111. }
  112.  
  113. /*
  114.   sanitize privte data by delete the files session.rdf session.old
  115. */
  116. var TMP_Sanitizer = {
  117.    _prefService: null,
  118.    get prefService() {
  119.      if (!this._prefService) {
  120.        this._prefService = Components.classes["@mozilla.org/preferences-service;1"]
  121.                         .getService(Components.interfaces.nsIPrefBranch2);
  122.      }
  123.      return this._prefService;
  124.    },
  125.  
  126.    _isFirefox35: null,
  127.    get isFirefox35() {
  128.      if (this._isFirefox35 == null) {
  129.        var appInfo = Components.classes["@mozilla.org/xre/app-info;1"]
  130.                          .getService(Components.interfaces.nsIXULAppInfo);
  131.        var versionChecker = Components.classes["@mozilla.org/xpcom/version-comparator;1"]
  132.                          .getService(Components.interfaces.nsIVersionComparator);
  133.        this._isFirefox35 = versionChecker.compare(appInfo.version, "3.1a2") > 0;
  134.      }
  135.      return this._isFirefox35;
  136.    },
  137.  
  138.    addSanitizeItem: function () {
  139.       if (typeof Sanitizer != 'function')
  140.          return;
  141.       // Sanitizer will execute this
  142.       Sanitizer.prototype.items['extensions-tabmix'] = {
  143.          clear : function() {
  144.             try {
  145.                TMP_Sanitizer.sanitize();
  146.             } catch (ex) {
  147.                try { Components.utils.reportError(ex); } catch(ex) {}
  148.             }
  149.          },
  150.          get canClear() {
  151.             return true;
  152.          }
  153.       }
  154.    },
  155.  
  156.    addMenuItem: function () {
  157.       var prefs = document.getElementsByTagName("preferences")[0];
  158.       var _item;
  159.       var itemList = document.getElementById("itemList");
  160.       if (itemList)
  161.         _item = itemList.lastChild;
  162.       else {
  163.         _item = document.getElementsByTagName("checkbox");
  164.         _item = _item[this.isFirefox35 ? _item.length - 1 : 0];
  165.       }
  166.       if (prefs && _item) {// if this isn't true we are lost :)
  167.         var prefName;
  168.         if (this.isFirefox35) {
  169.           var cpd = _item.getAttribute("preference").indexOf("privacy.cpd.") != -1;
  170.           if (cpd)
  171.             prefName = "privacy.cpd.extensions-tabmix";
  172.           else
  173.             prefName = "privacy.clearOnShutdown.extensions-tabmix";
  174.          }
  175.          else
  176.            prefName = "privacy.item.extensions-tabmix";
  177.  
  178.          var pref = document.createElement("preference");
  179.          pref.setAttribute("id", prefName);
  180.          pref.setAttribute("name", prefName);
  181.          pref.setAttribute("type", "bool");
  182.          prefs.appendChild(pref);
  183.  
  184.          var check = document.createElement(itemList ? "listitem" : "checkbox");
  185.          check.setAttribute("label", tabmixSanitize.label);
  186.          check.setAttribute("accesskey", tabmixSanitize.accesskey);
  187.          check.setAttribute("preference", prefName);
  188.          check.setAttribute("oncommand", "TMP_Sanitizer.confirm(this);");
  189.          if (itemList) {
  190.            check.setAttribute("type", "checkbox");
  191.            itemList.appendChild(check);
  192.          }
  193.          else
  194.            _item.parentNode.insertBefore(check, this.isFirefox35 ? null : _item);
  195.  
  196.          if (typeof(gSanitizePromptDialog) == "object") {
  197.             pref.setAttribute("readonly", "true");
  198.             check.setAttribute("onsyncfrompreference", "return gSanitizePromptDialog.onReadGeneric();");
  199.          }
  200.       }
  201.    },
  202.  
  203.    confirm: function (aCheckbox) {
  204.       if (!aCheckbox.checked)
  205.          return;
  206.  
  207.       var promptService = Components.classes["@mozilla.org/embedcomp/prompt-service;1"]
  208.                                   .getService(Components.interfaces.nsIPromptService);
  209.  
  210.       var title = "Tab Mix Plus - " + document.title;
  211.       var msg = tabmixSanitize.confirm;
  212.       var buttonPressed = promptService.confirmEx(null,
  213.                      title,
  214.                      msg,
  215.                      (promptService.BUTTON_TITLE_YES * promptService.BUTTON_POS_0)
  216.                      + (promptService.BUTTON_TITLE_NO * promptService.BUTTON_POS_1),
  217.                      null, null, null, null, {});
  218.       if (buttonPressed == 1)
  219.          aCheckbox.checked = false;
  220.    },
  221.  
  222.    isSanitizeTMPwithoutPrompet: function (aOnExit) {
  223.      /*
  224.       * from Firefox 3.5 privacy.sanitize.promptOnSanitize was removed
  225.       *
  226.       * the new behavior is:
  227.       *   - Tools > Clear Recent History... - Always show the UI
  228.       *   - about:privatebrowsing clearing your recent history  - Always show the UI
  229.       *   - clear private data on exit - NEVER show the UI
  230.       */
  231.       var promptOnSanitize;
  232.       if (this.isFirefox35)
  233.         promptOnSanitize = !aOnExit;
  234.       else {
  235.         try {
  236.            promptOnSanitize = this.prefService.getBoolPref("privacy.sanitize.promptOnSanitize");
  237.         } catch (e) { promptOnSanitize = true;}
  238.       }
  239.  
  240.       // if promptOnSanitize is true we call TMP_Sanitizer.sanitize from Firefox Sanitizer
  241.       if (promptOnSanitize)
  242.          return false;
  243.  
  244.       try {
  245.          var sanitizeTabmix = this.prefService.getBoolPref("privacy.item.extensions-tabmix");
  246.       } catch (e) { sanitizeTabmix = false;}
  247.  
  248.       return sanitizeTabmix;
  249.    },
  250.  
  251.    tryToSanitize: function (aOnExit) {
  252.       if (this.isSanitizeTMPwithoutPrompet(aOnExit)) {
  253.         this.sanitize();
  254.         return true;
  255.       }
  256.  
  257.       // History sanitize remove closed tab data from session restore
  258.       // we need to update closed tab button state
  259.       var w = getTopWin();
  260.       if (w && w.TMP_ClosedTabs.ssIsON) {
  261.          var _windows = windowEnumerator();
  262.          while ( _windows.hasMoreElements() ) {
  263.             _windows.getNext().TMP_ClosedTabs.setButtonDisableState();
  264.          }
  265.       }
  266.  
  267.       return false;
  268.    },
  269.  
  270. // XXX need to add test if we fail to delete then alert the user or ....?
  271.    sanitize: function TMP_SN_sanitize() {
  272.       // get file references
  273.       var dirService = Components.classes["@mozilla.org/file/directory_service;1"]
  274.                            .getService(Components.interfaces.nsIProperties);
  275.       var sessionFile = dirService.get("ProfD", Components.interfaces.nsILocalFile);
  276.       var sessionFileBackup = sessionFile.clone();
  277.       var sessionsBackupDir = sessionFile.clone()
  278.       sessionFile.append("session.rdf");
  279.       sessionFileBackup.append("session.old");
  280.       sessionsBackupDir.append("sessionbackups");
  281.  
  282.       // remove the files from the disk
  283.       this.clearDisk(sessionFile);
  284.       this.clearDisk(sessionFileBackup);
  285.       this.clearDisk(sessionsBackupDir);
  286.  
  287.       // init new DATASource for all open window
  288.       var enumerator = windowEnumerator();
  289.       var wnd, _sessionManager, broadcaster, btn;
  290.       while ( enumerator.hasMoreElements() ) {
  291.          wnd = enumerator.getNext();
  292.  
  293.          // clear DATASource
  294.          delete wnd.gBrowser.windowID;
  295.          _sessionManager = wnd.SessionManager;
  296.          _sessionManager.corruptedFile = false;
  297.          _sessionManager.RDFService.UnregisterDataSource(_sessionManager.DATASource);
  298.          // init new DATASource
  299.          _sessionManager.initDATASource();
  300.  
  301.          // disable closed window list button
  302.          broadcaster = wnd.document.getElementById("tmp_closedwindows");
  303.          if (broadcaster)
  304.             broadcaster.setAttribute("disabled",true);
  305.  
  306.          // clear closed tabs and disable the button if we use TMP session manager and save close tabs
  307.          if ((SessionManager.enableManager || SessionManager.enableBackup) && SessionManager.saveClosedTabs) {
  308.             wnd.TMP_ClosedTabs.restoreTab("original", -1);
  309.             wnd.TMP_ClosedTabs.setButtonDisableState();
  310.          }
  311.       }
  312.  
  313.       // set flag for next start
  314.       SessionPref.setBoolPref("sanitized" , true);
  315.       window.setTimeout( function () {
  316.          SessionManager.setLiteral(gRDFRoot + "/closedSession/thisSession", "status", "sanitized");
  317.  
  318.          // Make sure to break session manager cycle with the save timer
  319.          if (SessionManager._saveTimer) {
  320.             SessionManager._saveTimer.cancel();
  321.             SessionManager._saveTimer = null;
  322.          }
  323.          SessionManager.saveState();
  324.       }, 0 );
  325.    },
  326.  
  327.    clearDisk: function (aFile) {
  328.       if (aFile.exists()) {
  329.          try {
  330.             aFile.remove(aFile.isDirectory());
  331.          }
  332.          catch (ex) { dump(ex + "\n"); TMP_ASSERT(ex);} // couldn't remove the file - what now?
  333.       }
  334.    }
  335.  
  336. }
  337.  
  338. var SessionData = {
  339.    docShellItems: ["Images","Subframes","MetaRedirects","Plugins","Javascript"],
  340.    tabAttribute:  ["protected","locked"],
  341.  
  342.    getTabProperties: function sData_getTabProperties(aTab, checkPref) {
  343.       if (typeof(checkPref) == "undefined") checkPref = false; // pref check is only for session manager
  344.       var tabProperties = "", temp;
  345.       for ( var j = 0; j < this.tabAttribute.length; j++ ){
  346.          temp = aTab.hasAttribute(this.tabAttribute[j]) ? aTab.getAttribute(this.tabAttribute[j]) : "false";
  347.          tabProperties += (temp=="true") ? "1" : "0";
  348.       }
  349.       // if save.permissions is false we save all Permissions as on, so if we change this pref after session
  350.       // was saved, the session will load with Permissions as on.
  351.       if (checkPref && !SessionPref.getBoolPref("save.permissions"))
  352.         tabProperties += "11111";
  353.       else {
  354.          var aTabDocShell = gBrowser.getBrowserForTab(aTab).docShell;
  355.          for ( j = 0; j < this.docShellItems.length; j++ ){
  356.             tabProperties += aTabDocShell["allow" + this.docShellItems[j]] ? "1" : "0";
  357.          }
  358.       }
  359.  
  360.       // save fixed label data
  361.       if (aTab.hasAttribute("fixed-label")) {
  362.          temp = " fixed-label=" + encodeURI(aTab.getAttribute("fixed-label"));
  363.          temp += " label-uri=" + encodeURI(aTab.getAttribute("label-uri"));
  364.          tabProperties += temp;
  365.       }
  366.  
  367.       // save reload data
  368.       if (aTab.getAttribute("reload-data")) {
  369.           tabProperties += " reload-data=" + encodeURI(aTab.getAttribute("reload-data"));
  370.       }
  371.  
  372.       // save data for bookmark tab title
  373.       if (aTab.getAttribute("tabmix_bookmarkId")) {
  374.           tabProperties += " tabmix_bookmarkId=" + encodeURI(aTab.getAttribute("tabmix_bookmarkId"));
  375.       }
  376.  
  377.       return tabProperties;
  378.    },
  379.  
  380.    setTabProperties: function(aTab, tabProperties, checkPref) {
  381.       var booleanAttrLength = this.tabAttribute.length + this.docShellItems.length;
  382.       if (tabProperties.length > booleanAttrLength) {
  383.          var tabData = {xultab: ""};
  384.          tabData.xultab = tabProperties.substr(booleanAttrLength + 1);
  385.          var fixedLabel = TMP_SessionStore._getAttribute(tabData, "fixed-label");
  386.          if (fixedLabel) {
  387.             aTab.setAttribute("fixed-label", fixedLabel);
  388.             aTab.setAttribute("label-uri", TMP_SessionStore._getAttribute(tabData, "label-uri"));
  389.          }
  390.          var reloadData = TMP_SessionStore._getAttribute(tabData, "reload-data");
  391.          if (reloadData) {
  392.             aTab.setAttribute("reload-data", reloadData);
  393.             reloadData = reloadData.split(" ");
  394.             setupAutoReload(aTab);
  395.             aTab.autoReloadEnabled = true;
  396.             aTab.autoReloadURI = reloadData[0];
  397.             aTab.autoReloadTime = reloadData[1];
  398.          }
  399.          var bmitemid = TMP_SessionStore._getAttribute(tabData, "tabmix_bookmarkId");
  400.          if (bmitemid) {
  401.             aTab.setAttribute("tabmix_bookmarkId", bmitemid);
  402.          }
  403.       }
  404.  
  405.       if (typeof(checkPref) == "undefined") checkPref = false; // pref check is only for session manager
  406.       tabProperties = tabProperties.substr(0, booleanAttrLength);
  407.       var k = this.tabAttribute.length;
  408.       for ( var j = 0; j < k; j++ ){
  409.          //extensions.tabmix.sessions.save.protected && extensions.tabmix.sessions.save.locked
  410.          var attrib = this.tabAttribute[j];
  411.          if (!checkPref || SessionPref.getBoolPref("save." + attrib)) {
  412.             TMP_setItem(aTab, attrib, tabProperties.charAt(j) == "1" || null);
  413.          }
  414.       }
  415.       if (alwaysNewTab == 1 || aTab.hasAttribute("locked"))
  416.          TMP_setItem(aTab, "_locked", aTab.hasAttribute("locked"));
  417.  
  418.       if (checkPref && !SessionPref.getBoolPref("save.permissions")) return;
  419.       var aPermission;
  420.       var aTabDocShell = gBrowser.getBrowserForTab(aTab).docShell;
  421.       for ( j = 0; j < this.docShellItems.length; j++ ) {
  422.          aPermission = tabProperties.charAt(j + k) == "1";
  423.          if (aTabDocShell["allow" + this.docShellItems[j]] != aPermission)
  424.             aTabDocShell["allow" + this.docShellItems[j]] = aPermission;
  425.       }
  426.    }
  427. }
  428.  
  429. function windowEnumerator(aWindowtype) {
  430.   if (typeof(aWindowtype) == "undefined")
  431.      aWindowtype = "navigator:browser";
  432.   var windowMediator = Components.classes['@mozilla.org/appshell/window-mediator;1']
  433.                         .getService(Components.interfaces.nsIWindowMediator);
  434.   return windowMediator.getEnumerator(aWindowtype);
  435. }
  436.  
  437. function numberOfWindows(all, aWindowtype) {
  438.   var enumerator = windowEnumerator(aWindowtype);
  439.   var count = 0;
  440.   while ( enumerator.hasMoreElements() ) {
  441.     var win = enumerator.getNext();
  442.     if ("SessionManager" in win && win.SessionManager.windowClosed)
  443.       continue;
  444.     count++;
  445.     if (!all && count == 2)
  446.       break;
  447.   }
  448.   return count;
  449. }
  450.  
  451. var SessionManager = {
  452.     RDFService: null,
  453.     CONUtils: null,
  454.     DATASource: null,
  455.     IOService: null,
  456.     overwriteWindow: false,
  457.     saveThisWindow: true,
  458.     NC_NS : "http://home.netscape.com/NC-rdf#",
  459.     enableBackup: null,
  460.     enableManager: null,
  461.     enableSaveHistory: null,
  462.     saveClosedtabs: null,
  463.     corruptedFile: false,
  464.     afterTabSwap: false,
  465.  
  466.     // whether we are in private browsing mode (from firefox 3.5)
  467.     _inPrivateBrowsing: false,
  468.  
  469.     // call by TM_init
  470.    init: function SM_init() {
  471.       var _afterTabduplicated = "tabmix_afterTabduplicated" in window && window.tabmix_afterTabduplicated;
  472.       var isFirstWindow = numberOfWindows() == 1 && !_afterTabduplicated;
  473.       /* Add attribute to nsSessionStore persistTabAttribute after delay
  474.          we call this after nsSessionStore.init
  475.          we add this also when we use TMP session manager.
  476.          we use Firefox SessionStore closed tab service and for restore after restart
  477.       */
  478.       if (isFirstWindow)
  479.         TMP_SessionStore.persistTabAttribute();
  480.  
  481.       // private browsing - from firefox 3.5
  482.       // closedwindows menu - from firefox 3.5
  483.       if (gIsFirefox35) {
  484.         var observerService = Cc["@mozilla.org/observer-service;1"].
  485.                             getService(Ci.nsIObserverService);
  486.         observerService.addObserver(this, "browser-window-change-state", true);
  487.         observerService.addObserver(this, "private-browsing", true);
  488.         observerService.notifyObservers(null, "browser-window-change-state", "opened");
  489.  
  490.         observerService.addObserver(this, "quit-application-requested", true);
  491.         observerService.addObserver(this, "browser-lastwindow-close-requested", true);
  492.       }
  493.  
  494.       this.enableManager = SessionPref.getBoolPref("manager") && !this._inPrivateBrowsing;
  495.       this.enableBackup = SessionPref.getBoolPref("crashRecovery") && !this._inPrivateBrowsing;
  496.       this.enableSaveHistory = SessionPref.getBoolPref("save.history");
  497.       this.saveClosedtabs = SessionPref.getBoolPref("save.closedtabs") &&
  498.                              tabxPrefs.getBoolPref("undoClose");
  499.       this._lastSaveTime = Date.now();
  500.       // check if we need to backup
  501.       if (isFirstWindow && this.enableManager && !SessionPref.prefHasUserValue("sanitized")) {
  502.          try {
  503.            this.archiveSessions();
  504.          }
  505.          catch (ex) {TMP_ASSERT(ex);}
  506.       }
  507.  
  508.       if (!this.DATASource || isFirstWindow)
  509.          this.initService();
  510.  
  511.       if (this._inPrivateBrowsing) {
  512.          this.updateSettings();
  513.          this.setLiteral(gThisWin, "dontLoad", "true");
  514.          return;
  515.       }
  516.  
  517.       var path, status, caller, crashed;
  518.       if (isFirstWindow) {
  519.          path = gRDFRoot + "/closedSession/thisSession";
  520.          status = this.getLiteralValue(path, "status");
  521.          crashed = status.indexOf("crash") != -1;
  522.          // if this isn't delete on exit, we know next time that firefox crash
  523.          SessionPref.setBoolPref("crashed" , true); // we use this in setup.js;
  524.          nsIPrefServiceObj.savePrefFile(null); // store the pref immediately
  525.          this.setLiteral(path, "status", "crash");
  526.  
  527.          // if we after sanitize, we have no data to restore
  528.          if (this.enableManager && SessionPref.prefHasUserValue("sanitized")) {
  529.             SessionPref.clearUserPref("sanitized");
  530.             this.loadHomePage();
  531.             this.saveStateDelayed();
  532.             return;
  533.          }
  534.  
  535.          if (!this.enableManager && (!this.enableBackup || !crashed)) {
  536.             return;
  537.          }
  538.  
  539.          if (crashed)
  540.             this.openAfterCrash(status);
  541.          else if (this.enableManager) {
  542.             // check if sessionStore restore the session after restart we do not need to do anything
  543.             var afterRestart = TMP_SessionStore.isAfterRestart();
  544.             if (afterRestart)
  545.               this.onSessionRestored();
  546.             else
  547.                this.openFirstWindow(false);
  548.          }
  549.  
  550.          if (tabxPrefs.prefHasUserValue("warnAboutClosingTabs.timeout"))
  551.             tabxPrefs.clearUserPref("warnAboutClosingTabs.timeout")
  552.       }
  553.       else if (this.enableManager && "tabmixdata" in window) {
  554.          path = window.tabmixdata.path;
  555.          caller = window.tabmixdata.caller;
  556.  
  557.          if (caller == "concatenatewindows")
  558.             this.loadSession(path, caller, false);
  559.          else
  560.             this.loadOneWindow(path, "windowopenebytabmix");
  561.       }
  562.       // sync rdf list with sessionstore closed tab after restart
  563.       // we need it when we delete/restore close tab
  564.       // we need this in case that more then one window where opened before restart
  565.       else if (this.enableManager && this.enableBackup && this.saveClosedtabs && TMP_ClosedTabs.count > 0) {
  566.          this.initSession(gSessionPath[0], gThisWin);
  567.          this.copyClosedTabsToRDF(gThisWin);
  568.       }
  569.  
  570.       // initialize closed window list broadcaster
  571.       if (this.enableManager)
  572.          document.getElementById("tmp_closedwindows").setAttribute("disabled",this.isClosedWindowsEmpty());
  573.  
  574.       this.saveStateDelayed();
  575.    },
  576.  
  577.    // we call this function after session restored by sessionStore, after restart or after exit private-browsing
  578.    onSessionRestored: function SM_onSessionRestored() {
  579.       // sync rdf list with sessionstore closed tab after restart
  580.       // we need it when we delete/restore close tab
  581.       if (this.enableBackup && this.saveClosedtabs && TMP_ClosedTabs.count > 0) {
  582.          this.initSession(gSessionPath[0], gThisWin);
  583.          this.copyClosedTabsToRDF(gThisWin);
  584.       }
  585.  
  586.       // we keep the old session after restart.
  587.       // just remove the restore session from close window list
  588.       var sessionContainer = this.initContainer(gSessionPath[0]);
  589.       this.deleteWithProp(sessionContainer, "status", "saved");
  590.       // all the windows that opened by restart will save again with new windowID
  591.       // mark all current data with dontLoad flag
  592.       var rdfNodeThisWin = this.RDFService.GetResource(gThisWin);
  593.       var windowEnum = sessionContainer.GetElements();
  594.       while (windowEnum.hasMoreElements()) {
  595.          var rdfNodeWindow = windowEnum.getNext();
  596.          // skip this window....
  597.          if (rdfNodeThisWin == rdfNodeWindow)
  598.             continue;
  599.          this.setLiteral(rdfNodeWindow, "dontLoad", "true");
  600.       }
  601.    },
  602.  
  603.    // call by TMP_eventListener.onWindowClose and SessionManager.canQuitApplication, see tablib.js for more
  604.    deinit: function SM_deinit(aLastWindow, askBeforSave, aPopUp) {
  605.       // When Exit Firefox:
  606.       //       pref "extensions.tabmix.sessions.onClose"
  607.       //       0 - Save
  608.       //       1 - Ask me before Save
  609.       //       2 (or else) - Don't Save
  610.       // we check this when last window is about to close for all other window the session is saved
  611.       // in closed window list.
  612.       // in the last window if the user pref in not to save we delete the closed window list.
  613.       var resultData = {canClose: true, showMorePrompt: true, saveSession: true, removeClosedTabs: false};
  614.       if (this.windowClosed || this._inPrivateBrowsing)
  615.          return resultData;
  616.  
  617.       // we set aPopUp only in canQuitApplication
  618.       if (aPopUp == null)
  619.         aPopUp = TMP_checkForPopup(window);
  620.  
  621.       this.lastSaveTabsCount = this.saveOnWindowClose();
  622.       if (!aLastWindow) {
  623.         if (!aPopUp) {
  624.            var thisWinSaveTime = this.getLiteralValue(gThisWin, "timestamp", 0);
  625.            this.setLiteral(gSessionPath[0], "timestamp", thisWinSaveTime);
  626.         }
  627.         return resultData;
  628.       }
  629.       // we are on the last window........
  630.  
  631.       // we call TMP_Sanitizer.tryToSanitize from TM_deinit
  632.       // we don't need to show warnBeforSaveSession dialog if we sanitize TMP without prompet on exit
  633.       if (gTabmixPrefs.getBoolPref("privacy.sanitize.sanitizeOnShutdown") && TMP_Sanitizer.isSanitizeTMPwithoutPrompet(true))
  634.          return resultData;
  635.  
  636.       if ( this.enableManager ) {
  637.          var result = {button: SessionPref.getIntPref("onClose"), checked: this.saveClosedtabs};
  638.          if (result.button == 1 && !askBeforSave)
  639.             result.button = BUTTON_OK;
  640.          var sessionNotEmpty = this.updateClosedWindowList(aPopUp);
  641.          if (sessionNotEmpty && result.button == 1) { // Ask me before Save
  642.             // result:  0 - save; 1 - cancel quit; 2 - don't save
  643.             result = this.warnBeforSaveSession();
  644.             resultData.showMorePrompt = false;
  645.             if (result.button == BUTTON_CANCEL) {
  646.                resultData.canClose = false;
  647.                return resultData;
  648.             }
  649.          }
  650.          resultData.saveSession = result.button == BUTTON_OK;
  651.          resultData.removeClosedTabs = this.saveClosedTabs && !result.checked;
  652.       }
  653.       return resultData;
  654.    },
  655.  
  656.    saveOnWindowClose: function SM_saveOnWindowClose() {
  657.       if (this.enableManager && this.saveThisWindow) {
  658.          for (var i = 0; i < gBrowser.mTabs.length; i++)
  659.             gBrowser.mTabs[i].removeAttribute("inrestore");
  660.          return this.saveOneWindow(gSessionPath[0], "windowclosed");
  661.       }
  662.       return 0;
  663.    },
  664.  
  665.    warnBeforSaveSession: function SM_warnBeforSaveSession() {
  666.       window.focus();
  667.       var bundle_session = document.getElementById("bundle_session_manager");
  668.       var title = bundle_session.getString("sm.askBeforSave.title");
  669.       var msg = bundle_session.getString("sm.askBeforSave.msg0") + "\n\n"
  670.                 + bundle_session.getString("sm.askBeforSave.msg1");
  671.       var chkBoxLabel = bundle_session.getString("sm.saveClosedTab.chkbox.label");
  672.       var chkBoxState = this.saveClosedTabs ? CHECKBOX_CHECKED : HIDE_CHECKBOX;
  673.  
  674.       var stringBundle = Components.classes["@mozilla.org/intl/stringbundle;1"]
  675.                          .getService(Components.interfaces.nsIStringBundleService)
  676.                          .createBundle("chrome://global/locale/commonDialogs.properties");
  677.       var buttons = this.setLabel("sm.askBeforSave.button0")
  678.                      + "\n" + stringBundle.GetStringFromName("Cancel")
  679.                      + "\n" + this.setLabel("sm.askBeforSave.button1");
  680.       return TM_PromptService([BUTTON_OK, HIDE_MENUANDTEXT, chkBoxState],
  681.                               [title, msg, "", chkBoxLabel, buttons]);
  682.    },
  683.  
  684.    windowIsClosing: function SM_WindowIsClosing(aCanClose, aLastWindow, aSaveSession, aRemoveClosedTabs) {
  685.       if (this.windowClosed || this._inPrivateBrowsing)
  686.          return;
  687.  
  688.       this.windowClosed = aCanClose;
  689.       var _flush = false;
  690.  
  691.       // remove the "dontLoad" flag from current window if user cancel the close
  692.       if (this.enableManager && !aCanClose && this.removeAttribute(gThisWin, "dontLoad"))
  693.         _flush = true;
  694.  
  695.       // update status flag for this window
  696.       // when enableManager is off lastSaveTabsCount is undefined
  697.       if (!aLastWindow && this.lastSaveTabsCount && aCanClose) {
  698.          this.setLiteral(gThisWin, "status", "saved");
  699.          this.updateClosedWindowsMenu(false);
  700.          _flush = true;
  701.       }
  702.  
  703.       if (aLastWindow && aCanClose) {
  704.          if (this.enableManager) {
  705.             if (aSaveSession) {
  706.                var rdfNodeClosedWindows = this.RDFService.GetResource(gSessionPath[0]);
  707.                var sessionContainer = this.initContainer(rdfNodeClosedWindows);
  708.                this.deleteWithProp(sessionContainer, "dontLoad");
  709.                var count = this.countWinsAndTabs(sessionContainer);
  710.                this.setLiteral(rdfNodeClosedWindows, "nameExt", this.getNameData(count.win, count.tab));
  711.                // delete closed tab list for this session
  712.                if (aRemoveClosedTabs)
  713.                  this.deleteAllClosedtabs(sessionContainer);
  714.             }
  715.             else // delete ALL closed window list.
  716.                this.deleteSubtree(gSessionPath[0]);
  717.          }
  718.          // clean-up....
  719.          if (this.enableBackup) this.deleteSession(gSessionPath[3]);
  720.          if (tabxPrefs.prefHasUserValue("warnAboutClosingTabs.timeout"))
  721.             tabxPrefs.clearUserPref("warnAboutClosingTabs.timeout");
  722.          if (SessionPref.prefHasUserValue("crashed"))
  723.             SessionPref.clearUserPref("crashed"); // we use this in setup.js;
  724.          nsIPrefServiceObj.savePrefFile(null); // store the pref immediately
  725.          this.setLiteral(gRDFRoot + "/closedSession/thisSession", "status", "stopped");
  726.          if (!this.enableManager && !this.enableBackup)
  727.             this.deleteSession(gSessionPath[0]);
  728.          _flush = true;
  729.       }
  730.       if (_flush || this._saveTimer) {
  731.          // Make sure to break session manager cycle with the save timer
  732.          if (this._saveTimer) {
  733.            this._saveTimer.cancel();
  734.            this._saveTimer = null;
  735.          }
  736.          this.saveState();
  737.       }
  738.    },
  739.  
  740.    // For Firefox 3.0+ call from goQuitApplication when user apply File > Exit
  741.    // or when extensions (Mr Tech Toolkit) call goQuitApplication.
  742.    canQuitApplication: function SM_canQuitApplication(aBackup) {
  743.        // some extension can call goQuitApplication 2nd time (like toolKit)
  744.        // we make sure not to run this more the one time
  745.        if (this.windowClosed || this._inPrivateBrowsing)
  746.           return true;
  747.       /*
  748.         1. save all window
  749.         2. call deinit to the current window (if exist ??)
  750.         3 if user don't cancel the quit mark all windows as closed
  751.         4. return: true if its ok to close
  752.                    false if user cancel quit
  753.       */
  754.       this.saveAllWindows(gSessionPath[0], "windowclosed", true);
  755.       // cheack if all open windows are popup
  756.       var allPopups = TMP_checkForPopup(window);
  757.       var wnd, enumerator;
  758.       enumerator = windowEnumerator();
  759.       while ( allPopups && enumerator.hasMoreElements() ) {
  760.          wnd = enumerator.getNext();
  761.          allPopups = TMP_checkForPopup(wnd);
  762.       }
  763.       var result = this.deinit(true, !aBackup, allPopups); // we fake that we are the last window
  764.       this.windowIsClosing(result.canClose, true, result.saveSession, result.removeClosedTabs);
  765.  
  766.       if (result.canClose) {
  767.          enumerator = windowEnumerator();
  768.          while ( enumerator.hasMoreElements() ) {
  769.             wnd = enumerator.getNext();
  770.             wnd.SessionManager.windowClosed = true;
  771.          }
  772.       }
  773.       return result.canClose;
  774.    },
  775.  
  776.    // XXX split this for each pref that has change
  777.    // XXX need to update after permissions, locked......
  778.    updateSettings: function SM_updateSettings() {
  779.       // list of session manager pref
  780.       //          sessions.manager - ok
  781.       //          sessions.crashRecovery - ok
  782.       //          sessions.save.closedtabs - ok
  783.       //          sessions.save.history - ok
  784.       //          sessions.save.permissions - ok (update evry time this function run because lock is change)
  785.       //          sessions.save.locked - ok (update evry time this function run because lock is change)
  786.       //          sessions.save.protected - ok (update evry time this function run because lock is change)
  787.       //          sessions.save.selectedtab - ok
  788.       // xxx      sessions.save.scrollposition - ok (update with history) // xxx need to divide it
  789.       //          undoClose -
  790.       //          browser.sessionstore.max_tabs_undo
  791.       //
  792.       var sessionManager = tabxPrefs.getBoolPref("sessions.manager") && !this._inPrivateBrowsing;
  793.       var crashRecovery = tabxPrefs.getBoolPref("sessions.crashRecovery") && !this._inPrivateBrowsing;
  794.       var enableClosedtabs = tabxPrefs.getBoolPref("sessions.save.closedtabs");
  795.       var enableSaveHistory = tabxPrefs.getBoolPref("sessions.save.history");
  796.       var undoClose = tabxPrefs.getBoolPref("undoClose");
  797.       var maxTabsUndo = gTabmixPrefs.getIntPref("browser.sessionstore.max_tabs_undo");
  798.  
  799.        // hide or show session manager buttons & menus
  800.       document.getElementById("tm-sessionmanager").hidden = !sessionManager || !tabxPrefs.getBoolPref("sessionToolsMenu");
  801.       var hiddenPref = !sessionManager || !tabxPrefs.getBoolPref("closedWinToolsMenu");
  802.       document.getElementById("tm-sm-closedwindows").hidden = hiddenPref;
  803.       document.getElementById("tm-sm-closedwindows2").hidden = !hiddenPref;
  804.  
  805.       TMP_setItem("tmp_sessionmanagerButton", "disabled", !sessionManager || null);
  806.  
  807.       // we dont need this function to run before sessionmanager init
  808.       // this.enableBackup=null so we dont past the next if
  809.       if (!this.DATASource) return;
  810.       var windowSaved = false, closedTabSaved = false;
  811.       if (this.enableBackup != crashRecovery) {
  812.          if (crashRecovery) { // save all open window and tab
  813.             this.saveAllWindows(gSessionPath[0], "windowbackup");
  814.             windowSaved = true;
  815.          } else { // remove all backup
  816.             this.deleteSession(gSessionPath[0], "status", "backup");
  817.             this.deleteSession(gSessionPath[3]);
  818.             this.initSession(gSessionPath[0], gThisWin);
  819.             this.saveStateDelayed();
  820.          }
  821.          this.enableBackup = crashRecovery;
  822.       }
  823.       var winPath = gThisWin;
  824.       if (crashRecovery)
  825.          this.tabSelected(); // this is fast so we dont check if the pref is changed ( just for now)
  826.       if (this.enableManager != sessionManager) {
  827.          this.enableManager = sessionManager;
  828.       }
  829.       // changing in browser.sessionstore.max_tabs_undo or undoClose pref maintained in TMP_PrefObserver observe
  830.       if (this.saveClosedtabs != enableClosedtabs && undoClose) {
  831.          this.saveClosedtabs = enableClosedtabs && undoClose;
  832.          if (enableClosedtabs) {
  833.             // save if gBrowser.closedTabs.length > 0 and we save backup and save closedtab backup
  834.             if (crashRecovery && TMP_ClosedTabs.count > 0 && undoClose) {
  835.                this.initSession(gSessionPath[0], winPath);
  836.                this.copyClosedTabsToRDF(winPath);
  837.             }
  838.             closedTabSaved = true;
  839.          } else {
  840.             // if undoClose = false we delete all in TMP_PrefObserver observe
  841.             if (undoClose) this.deleteWinClosedtabs(winPath); // flush only closedTabs list in session.RDF
  842.          }
  843.       }
  844.       if (this.enableSaveHistory != enableSaveHistory) {
  845.          this.enableSaveHistory = enableSaveHistory;
  846.          if (crashRecovery) {
  847.             if (!windowSaved) this.saveAllTab(winPath, 0);
  848.             if (!closedTabSaved && enableClosedtabs && TMP_ClosedTabs.count > 0 && undoClose) {
  849.                this.initSession(gSessionPath[0], winPath);
  850.                this.deleteWinClosedtabs(winPath);
  851.                this.copyClosedTabsToRDF(winPath);
  852.                closedTabSaved = true;
  853.             }
  854.          }
  855.       }
  856.       if (closedTabSaved) {
  857.          this.initSession(gSessionPath[0], gThisWin);
  858.          this.saveStateDelayed();
  859.       }
  860.    },
  861.  
  862.    loadHomePage: function SM_loadHomePage() {
  863.       var homePage = gHomeButton.getHomePage();
  864.       if ("arguments" in window && homePage == window.arguments[0]) {
  865.          this.setStripVisibility(homePage.split("|").length);
  866.          BrowserHome();
  867.          if (!gBrowser.isBlankBrowser(gBrowser.selectedBrowser))
  868.             content.focus();
  869.       }
  870.       else if (gBrowser.mCurrentTab.loadOnStartup)
  871.          for (var i = 0; i < gBrowser.mTabs.length ; i++)
  872.             delete gBrowser.mTabs[i].loadOnStartup;
  873.       else
  874.          gBrowser.selectedBrowser.reload();
  875.    },
  876.  
  877.     // init common services
  878.    initService: function() {
  879.       this.RDFService = Components.classes["@mozilla.org/rdf/rdf-service;1"]
  880.                        .getService(Components.interfaces.nsIRDFService);
  881.       this.CONUtils = Components.classes["@mozilla.org/rdf/container-utils;1"]
  882.                            .getService(Components.interfaces.nsIRDFContainerUtils);
  883.       this.setNC_TM();
  884.       this.initDATASource();
  885.    },
  886.  
  887.    initDATASource: function SM_initDATASource() {
  888.       var file = this.profileDir;
  889.       file.append("session.rdf");
  890.       var uri = gIOService.newFileURI(file).spec;
  891.       try {
  892.          this.DATASource = this.RDFService.GetDataSourceBlocking(uri);
  893.       } catch (e) { // corrupted session.rdf
  894.          var bundle_session = document.getElementById("bundle_session_manager");
  895.          var title = bundle_session.getString("sm.corrupted.title");
  896.          var msg = bundle_session.getString("sm.corrupted.msg0") + "\n"
  897.                   + bundle_session.getString("sm.corrupted.msg1");
  898.          var buttons = ["", this.setLabel("sm.button.continue")].join("\n");
  899.          TM_PromptService([BUTTON_CANCEL, HIDE_MENUANDTEXT, HIDE_CHECKBOX],[title, msg, "", "", buttons]);
  900.          TMP_ASSERT(e);
  901.          file.moveTo(this.profileDir, "session.old");
  902.          this.DATASource = this.RDFService.GetDataSourceBlocking(uri);
  903.          this.corruptedFile = true;
  904.       }
  905.  
  906.       // set path to session type
  907.       var path = gRDFRoot + "/closedSession/";
  908.       var sessionType = ["thisSession", "lastSession", "previoustolastSession", "crashedsession"];
  909.       var closedSession = this.initContainer(path);
  910.       var i, aEntry;
  911.       if (closedSession.GetCount()==0) { // create the list
  912.          for (i = 0; i < sessionType.length; i++) {
  913.             aEntry = this.RDFService.GetResource(path + sessionType[i]);
  914.             this.setResource(aEntry, "session", gRDFRoot + "/closed" + i + "/window");
  915.             closedSession.AppendElement(aEntry);
  916.          }
  917.       }
  918.       for (i = 0; i < sessionType.length; i++) {
  919.          gSessionPath[i] = this.getResourceValue(path + sessionType[i], "session");
  920.       }
  921.       if (typeof(gBrowser) == "object" && !gBrowser.windowID) {
  922.          gBrowser.windowID = this.getAnonymousId();
  923.          gThisWin = gSessionPath[0] + "/" + gBrowser.windowID;
  924.          gThisWinTabs = gThisWin + "/tabs";
  925.          gThisWinClosedtabs = gThisWin + "/closedtabs";
  926.       }
  927.    },
  928.  
  929.    get profileDir() {
  930.       return Components.classes["@mozilla.org/file/directory_service;1"]
  931.             .getService(Components.interfaces.nsIProperties)
  932.             .get("ProfD", Components.interfaces.nsILocalFile);
  933.    },
  934.  
  935.    getAnonymousId: function() {
  936.       const kSaltTable = [
  937.         'a', 'b', 'c', 'd', 'e', 'f', 'g', 'h', 'i', 'j', 'k', 'l', 'm', 'n',
  938.         'o', 'p', 'q', 'r', 's', 't', 'u', 'v', 'w', 'x', 'y', 'z',
  939.         'A', 'B', 'C', 'D', 'E', 'F', 'G', 'H', 'I', 'J', 'K', 'L', 'M', 'N',
  940.         'O', 'P', 'Q', 'R', 'S', 'T', 'U', 'V', 'W', 'X', 'Y', 'Z',
  941.         '1', '2', '3', '4', '5', '6', '7', '8', '9', '0' ];
  942.  
  943.       var id = "";
  944.       for (var i = 0; i < 8; ++i) {
  945.         id += kSaltTable[Math.floor(Math.random() * kSaltTable.length)];
  946.       }
  947.       return id;
  948.    },
  949.  
  950.    setNC_TM: function() {
  951.       var rdfLabels = ["tabs","closedtabs","index","history","properties","selectedIndex",
  952.                "timestamp","title","url","dontLoad","reOpened","name","nameExt","session",
  953.                "status","tabPos","image","scroll","winFeatures"];
  954.       for (var i = 0; i < rdfLabels.length; i++) {
  955.          NC_TM[rdfLabels[i]] = this.RDFService.GetResource(this.NC_NS + rdfLabels[i]);
  956.       }
  957.    },
  958.  
  959.    setLabel: function(property, bundleID) {
  960.       var strimgBundle = bundleID ? document.getElementById(bundleID) :
  961.             document.getElementById("bundle_session_manager");
  962.       var label = strimgBundle.getString(property + ".label");
  963.       var key = strimgBundle.getString(property + ".accesskey");
  964.       var accessKeyIndex = label.toLowerCase().indexOf(key.toLowerCase());
  965.       if (accessKeyIndex > -1)
  966.          label = label.substr(0, accessKeyIndex) + "&" + label.substr(accessKeyIndex);
  967.       return label;
  968.    },
  969.  
  970.    deleteNode: function(rdfNode) {
  971.       var arcOut = this.DATASource.ArcLabelsOut(rdfNode);
  972.       while (arcOut.hasMoreElements()) {
  973.          var aLabel = arcOut.getNext();
  974.          if (aLabel instanceof tmRDFResource) {
  975.             var aTarget = this.DATASource.GetTarget(rdfNode, aLabel, true);
  976.             this.DATASource.Unassert(rdfNode, aLabel, aTarget);
  977.          }
  978.       }
  979.    },
  980.  
  981.    deleteSubtree: function(labelRoot) {
  982.       var allElements = this.DATASource.GetAllResources();
  983.       while (allElements.hasMoreElements()) {
  984.          var aResource = allElements.getNext();
  985.          if ((aResource instanceof tmRDFResource) && (aResource.Value.indexOf(labelRoot) == 0))
  986.             this.deleteNode(aResource);
  987.       }
  988.    },
  989.  
  990.    initContainer: function(node) {
  991. var pNode = node;
  992. try{
  993.       if (typeof(node) == "string") node = this.RDFService.GetResource(node);
  994.       return this.CONUtils.MakeSeq(this.DATASource, node);
  995. } catch (e) {
  996.    TMP_ASSERT(e);
  997.    return "error"
  998. }
  999.    },
  1000.  
  1001.     // return true if node is empty container or node is not container
  1002.    containerEmpty: function(node) {
  1003. var pNode = node;
  1004. try{
  1005.       if (typeof(node) == "string") node = this.RDFService.GetResource(node);
  1006.       if (!this.CONUtils.IsContainer(this.DATASource, node)) return true;
  1007.       return this.CONUtils.IsEmpty(this.DATASource, node);
  1008. } catch (e) {
  1009.    TMP_ASSERT(e);
  1010.    return "error"
  1011. }
  1012.    },
  1013.  
  1014.    wrapContainer: function SM_wrapContainer(path, prop) {
  1015.       var root = this.getResource(path, prop);
  1016.       var container = this.initContainer(root);
  1017. if (container == "error") { tmLog("wrapContainer error path " + path + "\n" + "prop " + prop); return "error"}
  1018.       return {
  1019.          Root: root,
  1020.          Container: container,
  1021.          Enum: container.GetElements(),
  1022.          Count: container.GetCount()
  1023.       }
  1024.    },
  1025.  
  1026.    getValue: function(node, label, typeID, def) {
  1027.       if (typeof(node) == "string") node = this.RDFService.GetResource(node);
  1028.       label = NC_TM[label];
  1029.       var rdfNode = this.DATASource.GetTarget(node, label, true);
  1030.       return (rdfNode instanceof Components.interfaces[typeID]) ? rdfNode.Value : def;
  1031.    },
  1032.  
  1033.    getLiteralValue: function(node, arc, def) {
  1034.       if (typeof(def) == "undefined") def = "";
  1035.       return this.getValue(node, arc, "nsIRDFLiteral", def);
  1036.    },
  1037.  
  1038.    getIntValue: function(node, arc, def) {
  1039.       if (typeof(def) == "undefined") def = 0;
  1040.       return this.getValue(node, arc, "nsIRDFInt", def);
  1041.    },
  1042.  
  1043.    getResourceValue: function(node, arc, def) {
  1044.       if (typeof(def) == "undefined") def = null;
  1045.       return this.getValue(node, arc, "nsIRDFResource", def);
  1046.    },
  1047.  
  1048.    getResource: function(node, arc) {
  1049.       if (typeof(node) == "string") node = this.RDFService.GetResource(node);
  1050.       arc = NC_TM[arc];
  1051.       return this.DATASource.GetTarget(node, arc, true);
  1052.    },
  1053.  
  1054.    nodeHasArc: function(node, arc) {
  1055.       if (typeof(node) == "string") node = this.RDFService.GetResource(node);
  1056.       arc = NC_TM[arc];
  1057.       return this.DATASource.hasArcOut(node, arc);
  1058.    },
  1059.  
  1060.    setLiteral: function SM_setLiteral(node, arc, value) {
  1061.       if (typeof(node) == "string") node = this.RDFService.GetResource(node);
  1062.       arc = NC_TM[arc];
  1063.       value = this.RDFService.GetLiteral(value);
  1064.       this.changeValue(node, arc, value);
  1065.    },
  1066.  
  1067.    setIntLiteral: function(node, arc, value) {
  1068.       if (typeof(node) == "string") node = this.RDFService.GetResource(node);
  1069.       arc = NC_TM[arc];
  1070.       value = this.RDFService.GetIntLiteral(value);
  1071.       this.changeValue(node, arc, value);
  1072.    },
  1073.  
  1074.    setResource: function(node, arc, value) {
  1075.       if (typeof(node) == "string") node = this.RDFService.GetResource(node);
  1076.       arc = NC_TM[arc];
  1077.       if (typeof(value) == "string") value = this.RDFService.GetResource(value);
  1078.       this.changeValue(node, arc, value);
  1079.    },
  1080.  
  1081.    changeValue: function(node, arc, newValue) {
  1082.       if (this.DATASource.hasArcOut(node, arc)) {
  1083.          var oldValue = this.DATASource.GetTarget(node, arc, true);
  1084.          if (newValue != oldValue) this.DATASource.Change(node, arc, oldValue, newValue);
  1085.       } else this.DATASource.Assert(node, arc, newValue, true);
  1086.    },
  1087.  
  1088.    // use it only to remove node with literal value
  1089.    removeAttribute: function(node, arc) {
  1090.       if (typeof(node) == "string") node = this.RDFService.GetResource(node);
  1091.       if (this.nodeHasArc(node, arc)) {
  1092.          var value = this.getLiteralValue(node, arc);
  1093.          this.DATASource.Unassert(node, NC_TM[arc], this.RDFService.GetLiteral(value));
  1094.          return true;
  1095.       }
  1096.       return false; // arc not found
  1097.    },
  1098.  
  1099.    _saveTimer: null,
  1100.    // time in milliseconds (Date.now()) when the session was last written to file
  1101.    _lastSaveTime: 0,
  1102.    // minimal interval between two save operations (in milliseconds)
  1103.    _interval: 2000, // 10000
  1104.    saveStateDelayed: function SM_saveStateDelayed(aDelay) {
  1105.      if (!this._saveTimer) {
  1106.        // interval until the next disk operation is allowed
  1107.        var minimalDelay = this._lastSaveTime + this._interval - Date.now();
  1108.  
  1109.        // if we have to wait, set a timer, otherwise saveState directly
  1110.        aDelay = Math.max(minimalDelay, aDelay || 2000);
  1111.        if (aDelay > 0) {
  1112.          this._saveTimer = Cc["@mozilla.org/timer;1"].createInstance(Ci.nsITimer);
  1113.          this._saveTimer.init(this, aDelay, Ci.nsITimer.TYPE_ONE_SHOT);
  1114.        }
  1115.        else {
  1116.          this.saveState();
  1117.        }
  1118.      }
  1119.    },
  1120.  
  1121.    QueryInterface: function(aIID){
  1122.       if (aIID.equals(Components.interfaces.nsIObserver) ||
  1123.             aIID.equals(Components.interfaces.nsISupports) ||
  1124.             aIID.equals(Components.interfaces.nsISupportsWeakReference))
  1125.          return this;
  1126.       throw Components.results.NS_NOINTERFACE;
  1127.    },
  1128.  
  1129.    closeProtectedTabs: function(){
  1130.      var protectedTabs = gBrowser.mTabContainer.getElementsByAttribute("protected", true);
  1131.      for (var i = protectedTabs.length - 1 ; i >= 0; i--) {
  1132.        var tab = protectedTabs[i];
  1133.        tab.removeAttribute("protected");
  1134.        gBrowser.removeTab(tab);
  1135.      }   
  1136.    },
  1137.  
  1138.    observe: function SM_observe(aSubject, aTopic, aData) {
  1139.       switch (aTopic) {
  1140.          case "quit-application-requested":
  1141.          case "browser-lastwindow-close-requested":
  1142.             this.browserStartupPref = gTabmixPrefs.getIntPref("browser.startup.page");
  1143.             break;
  1144.          case "timer-callback": // timer call back for delayed saving
  1145.             this._saveTimer = null;
  1146.             this.saveState();
  1147.             break;
  1148.          case "browser-window-change-state":
  1149.             this.toggleRecentlyClosedWindowsButton();
  1150.             break;
  1151.          case "private-browsing":
  1152.             switch (aData) {
  1153.                case "enter":
  1154.                   // check if we need to close protected tab here
  1155.                   var needToCloseProtected = true;
  1156.                   try {
  1157.                     if (gTabmixPrefs.getBoolPref("browser.privatebrowsing.keep_current_session"))
  1158.                       needToCloseProtected = false;
  1159.                   } catch (ex) {}               
  1160.                   // noting to do here if we are not using tabmix session manager
  1161.                   if (!SessionPref.getBoolPref("manager") && !SessionPref.getBoolPref("crashRecovery")) {
  1162.                     // nsPrivateBrowsingService.js can not close protected tab we have to do it our self
  1163.                     // we only close this tab here after nsPrivateBrowsingService save the session
  1164.                     if (needToCloseProtected)
  1165.                       this.closeProtectedTabs();                  
  1166.                     this._inPrivateBrowsing = true;
  1167.                     TMP_ClosedTabs.setButtonDisableState(true);
  1168.                     this.toggleRecentlyClosedWindowsButton();
  1169.                     break;
  1170.                   }
  1171.                   // save curent state
  1172.                   this.canQuitApplication(true);
  1173.                   this._inPrivateBrowsing = true;
  1174.                   if (needToCloseProtected)
  1175.                     this.closeProtectedTabs();                  
  1176.                   this.enableManager = SessionPref.getBoolPref("manager") && !this._inPrivateBrowsing;
  1177.                   this.enableBackup = SessionPref.getBoolPref("crashRecovery") && !this._inPrivateBrowsing;
  1178.                   this.updateSettings();
  1179.                   TMP_ClosedTabs.setButtonDisableState(true);
  1180.                   break;
  1181.                case "exit":
  1182.                   // nsPrivateBrowsingService.js can not close protected tab we have to do it our self
  1183.                   this.closeProtectedTabs();               
  1184.                   aSubject.QueryInterface(Ci.nsISupportsPRBool);
  1185.                   var quitting = aSubject.data;
  1186.                   if (quitting)
  1187.                     break;
  1188.                   // build-in sessionStore restore the session for us
  1189.                   if (!SessionPref.getBoolPref("manager") && !SessionPref.getBoolPref("crashRecovery")) {
  1190.                     this._inPrivateBrowsing = false;
  1191.                     var self = this;
  1192.                     window.setTimeout(function () {
  1193.                       TMP_ClosedTabs.setButtonDisableState();
  1194.                       self.toggleRecentlyClosedWindowsButton();
  1195.                     }, 100);
  1196.                     break;
  1197.                   }
  1198.                   TMP_ClosedTabs.getClosedTabAtIndex(-1); // to be on the safe side...
  1199.                   window.setTimeout(function () {TMP_ClosedTabs.setButtonDisableState(); },gIsFirefox36 ? 0 : 100);
  1200.                   this.afterExitPrivateBrowsing = window.setTimeout(function (self) {
  1201.                     self._inPrivateBrowsing = false;
  1202.                     self.windowClosed = false;
  1203.                     self.onSessionRestored();
  1204.                     self.updateSettings();
  1205.                     self.removeAttribute(gThisWin, "dontLoad");
  1206.                     self.saveStateDelayed();
  1207.                     self.afterExitPrivateBrowsing = null;
  1208.                   },0, this);
  1209.                   break;
  1210.             }
  1211.          break;
  1212.       }
  1213.    },
  1214.  
  1215.    restoreWindow: function SM_restoreWindow(aWhere, aIndex) {
  1216.       switch (aWhere) {
  1217.          case "delete":
  1218.             this.forgetClosedWindow(aIndex);
  1219.             break;
  1220.          case "window":
  1221.          default:
  1222.             undoCloseWindow(aIndex);
  1223.             this.notifyClosedWindowsChanged();
  1224.       }
  1225.    },
  1226.  
  1227.   /**
  1228.    * @brief           catch middle click from closed windows list,
  1229.    *                  delete window from the list or resrore acurding to the pref
  1230.    * @param aEvent    a valid event union.
  1231.    * @returns         noting.
  1232.    *
  1233.    * we can use ss.forgetClosedWindow(index) from Firefox 3.7a1pre after 2009-08-14
  1234.    *
  1235.    */
  1236.    checkForMiddleClick: function SM_checkForMiddleClick(aEvent) {
  1237.       if (aEvent.button != 1)
  1238.          return;
  1239.  
  1240.       aEvent.stopPropagation();
  1241.       var index = "value" in aEvent.originalTarget ? aEvent.originalTarget.value : -1;
  1242.       if (index < 0)
  1243.          return;
  1244.  
  1245.       var where = tabxPrefs.getBoolPref("middleclickDelete") ? 'delete' : 'tab';
  1246.       SessionManager.restoreWindow(where, index);
  1247.       SessionManager.notifyClosedWindowsChanged();
  1248.       var popup = aEvent.originalTarget.parentNode;
  1249.       if (TMP_ClosedTabs.ss.getClosedWindowCount() > 0)
  1250.          HistoryMenu.populateUndoWindowSubmenu(popup.id);
  1251.       else {
  1252.          popup.hidePopup();
  1253.          if (popup.parentNode.id != "btn_closedwindows")
  1254.             popup.parentNode.parentNode.hidePopup();
  1255.       }
  1256.    },
  1257.  
  1258.    forgetClosedWindow: function SM_forgetClosedWindow(aIndex) {
  1259.      // from firefox 3.6+
  1260.      if ("forgetClosedWindow" in TMP_ClosedTabs.ss) {
  1261.        if (aIndex < 0) {
  1262.          while (TMP_ClosedTabs.ss.getClosedWindowCount() > 0) {
  1263.            TMP_ClosedTabs.ss.forgetClosedWindow(0);
  1264.          }
  1265.        }
  1266.        else
  1267.          TMP_ClosedTabs.ss.forgetClosedWindow(aIndex);
  1268.      }
  1269.      else
  1270.        this.getClosedWindowAtIndex(aIndex);
  1271.      this.notifyClosedWindowsChanged();
  1272.    },
  1273.  
  1274.   /**
  1275.    * @brief           fetch the data of closed window, while removing it from the array
  1276.    * @param aIndex    a Integer value - 0 or grater index to remove
  1277.    *                  other value empty the list.
  1278.    * @returns         closed window data at aIndex.
  1279.    *
  1280.    * we can use ss.forgetClosedWindow(index) from Firefox 3.6a2pre after 2009-08-14
  1281.    *
  1282.    */
  1283.    getClosedWindowAtIndex: function SM_getClosedWindowAtIndex(aIndex) {
  1284.       var closedWindow
  1285.       var state = { windows: [{tabs:[]}], _firstTabs: false , _closedWindows:[]};
  1286.       // if aIndex is not > 0 we just past empy list to setWindowState
  1287.       // it's like remove all closed tabs from the list
  1288.       if (aIndex >= 0) {
  1289.          state._closedWindows = eval("(" + TMP_ClosedTabs.ss.getClosedWindowData() + ")");
  1290.          // purge closed window at aIndex
  1291.          closedWindow = state._closedWindows.splice(aIndex, 1).shift();
  1292.       }
  1293.       // replace existing _closedWindows
  1294.       try {
  1295.         TMP_ClosedTabs.ss.setWindowState(window, state.toSource(), false);
  1296.       } catch (ex) {TMP_ASSERT(ex);}
  1297.  
  1298.       return closedWindow;
  1299.    },
  1300.  
  1301.    notifyClosedWindowsChanged: function SM_notifyClosedWindowsChanged() {
  1302.      var observerService = Cc["@mozilla.org/observer-service;1"].getService(Ci.nsIObserverService);
  1303.      observerService.notifyObservers(null, "browser-window-change-state", "changed");
  1304.    },
  1305.  
  1306.    // enable/disable the Recently Closed Windows button
  1307.    toggleRecentlyClosedWindowsButton: function SM_toggleRecentlyClosedWindowsButton() {
  1308.      var button = document.getElementById("btn_closedwindows");
  1309.      if (button && !this.enableManager) {
  1310.        // no restorable windows, so disable menu
  1311.        if (HistoryMenu._ss.getClosedWindowCount() == 0)
  1312.          button.setAttribute("disabled", true);
  1313.        else
  1314.          button.removeAttribute("disabled");
  1315.      }
  1316.    },
  1317.  
  1318.    saveState: function SM_saveState() {
  1319.       // if we're in private browsing mode, do nothing
  1320.       if (this._inPrivateBrowsing)
  1321.         return;
  1322.  
  1323.       try {
  1324.          this.DATASource.QueryInterface(Components.interfaces.nsIRDFRemoteDataSource).Flush();
  1325.          this._lastSaveTime = Date.now();
  1326.       }
  1327.       catch (ex) {
  1328.          TMP_ASSERT(ex, "Error when tabmix try to write to session.rdf file");
  1329.          if (this._interval < 10000) {
  1330.            this._interval += 500;
  1331.            this.saveStateDelayed();
  1332.          }
  1333.       }
  1334.    },
  1335.  
  1336.    promptReplaceStartup: function(caller, path) {
  1337.       var loadsession = SessionPref.getIntPref("onStart.loadsession");
  1338.       var sessionpath = SessionPref.getCharPref("onStart.sessionpath");
  1339.       var result = {button: NO_NEED_TO_REPLACE};
  1340.       if (loadsession < 0 || sessionpath != path) return result;
  1341.       var label = this.getLiteralValue(path, "name");
  1342.       var selectionFlag = SELECT_DEFAULT;
  1343.       var title, msg, buttons;
  1344.       var bundle_session = document.getElementById("bundle_session_manager");
  1345.       var areYouSure = bundle_session.getString("sm.areYouSure.msg");
  1346.       var chooseStartup = bundle_session.getString("sm.canChooseStartup.msg");
  1347.       switch ( caller ) {
  1348.          case "addWinToSession":
  1349.             title = bundle_session.getString("sm.addtoStartup.title");
  1350.             var msgType = caller=="addWinToSession" ? "windows" : "tabs";
  1351.             msg = bundle_session.getString("sm.addtoStartup.msg." + msgType) + "\n" + label
  1352.                +  "\n" + areYouSure + "\n\n" + chooseStartup;
  1353.             buttons = [this.setLabel("sm.addtoStartup.button0"),
  1354.                        this.setLabel("sm.addtoStartup.button1")].join("\n");
  1355.             break;
  1356.          case "replaceSession":
  1357.             title = bundle_session.getString("sm.replaceStartup.title");
  1358.             msg = bundle_session.getString("sm.replaceStartup.msg") + "\n" + label
  1359.                +  "\n" + areYouSure + "\n\n" + chooseStartup;
  1360.             buttons = [this.setLabel("sm.replaceStartup.button0"),
  1361.                        this.setLabel("sm.replaceStartup.button1")].join("\n");
  1362.             break;
  1363.          case "removeSavedSession":
  1364.             title = bundle_session.getString("sm.removeStartup.title");
  1365.             msg = bundle_session.getString("sm.removeStartup.msg0") + "\n" + label
  1366.                +  "\n" + areYouSure + "\n\n" + bundle_session.getString("sm.removeStartup.msg1");
  1367.             buttons = [this.setLabel("sm.removeStartup.button0"),
  1368.                        this.setLabel("sm.removeStartup.button1")].join("\n");
  1369.             selectionFlag = SELECT_LASTSESSION;
  1370.             break;
  1371.       }
  1372.       return TM_PromptService([BUTTON_OK, SHOW_MENULIST, HIDE_CHECKBOX, selectionFlag],
  1373.                   [title, msg, "", "", buttons]);
  1374.    },
  1375.  
  1376.    addWinToSession: function SM_addWinToSession(action) {
  1377.       if (!this.isValidtoSave()) return;
  1378.       var path = document.popupNode.session;
  1379.       var result = this.promptReplaceStartup("addWinToSession", path);
  1380.       if (result.button == BUTTON_CANCEL) return;
  1381.       else if (result.button == BUTTON_OK) this.replaceStartupPref(result, "");
  1382.       var saveClosedTabs = this.saveClosedtabs;
  1383.       var rdfNodeSession = this.RDFService.GetResource(path);
  1384.       var sessionContainer = this.initContainer(rdfNodeSession);
  1385.       var oldCount = this.countWinsAndTabs(sessionContainer);
  1386.       var newCount = this.saveOneOrAll(action, path, saveClosedTabs);
  1387.       if (newCount) {
  1388.          var numTabs = oldCount.tab + newCount.tab;
  1389.          var numWindows = oldCount.win + newCount.win;
  1390.          this.setLiteral(rdfNodeSession, "nameExt", this.getNameData(numWindows, numTabs));
  1391.          this.saveStateDelayed();
  1392.       }
  1393.    },
  1394.  
  1395.    saveClosedSession: function SM_saveClosedSession() {
  1396.       var oldPath = document.popupNode.session;
  1397.       var id = this.getAnonymousId();
  1398.       var path = gRDFRoot + "/saved/" + id + "/window";
  1399.       var pathToReplace = "";
  1400.       var session = this.getSessionName("saveprevious", this.getLiteralValue(oldPath, "name"));
  1401.       if (session.button == BUTTON_CANCEL) return; // user cancel
  1402.       else if (session.button == BUTTON_EXTRA1) { // we replace exist session, BUTTON_OK - save new session
  1403.          var result = this.promptReplaceStartup("replaceSession", session.path);
  1404.          if (result.button == BUTTON_CANCEL) return; // user cancel
  1405.          else if (result.button == BUTTON_OK) { // we replace startup session
  1406.             this.replaceStartupPref(result, path);
  1407.          }
  1408.          pathToReplace = session.path;
  1409.       }
  1410.       container = this.initContainer(path)
  1411.       var pathNode, container, extID = "";
  1412.       var node = document.popupNode.parentNode.parentNode;
  1413.       if (node.id.indexOf("tm-sm-closedwindows")==0 || node.id == "btn_closedwindows")
  1414.          extID = "/" + id;
  1415.       this.copySubtree(oldPath, path + extID);
  1416.       if (node.id.indexOf("tm-sm-closedwindows")==0 || node.id == "btn_closedwindows") {
  1417.          node = this.RDFService.GetResource(path + extID);
  1418.          container.InsertElementAt(node, 1, true);
  1419.          this.DATASource.Unassert(node, NC_TM["dontLoad"], this.RDFService.GetLiteral("true"));
  1420.       }
  1421.       var count = this.countWinsAndTabs(container); // we need it just to fix the date
  1422.       if (!session.saveClosedTabs)
  1423.          this.deleteAllClosedtabs(container);
  1424.       if (count)
  1425.          this.insertSession(count, session.name, path, pathToReplace);
  1426.       else
  1427.          tmLog("Error in saveClosedSession");
  1428.    },
  1429.  
  1430.    copyNode: function(oldNode, newNode, oldRoot, newRoot) {
  1431.       var newTarget;
  1432.       var arcOut = this.DATASource.ArcLabelsOut(oldNode);
  1433.       while (arcOut.hasMoreElements()) {
  1434.          var aLabel = arcOut.getNext();
  1435.          if (aLabel instanceof tmRDFResource) {
  1436.             var aTarget = this.DATASource.GetTarget(oldNode, aLabel, true);
  1437.             if (aTarget instanceof tmRDFResource) {
  1438.                newTarget = aTarget.Value.replace(oldRoot, newRoot);
  1439.                aTarget = this.RDFService.GetResource(newTarget);
  1440.             }
  1441.             this.DATASource.Assert(newNode, aLabel, aTarget, true);
  1442.          }
  1443.       }
  1444.    },
  1445.  
  1446.    copySubtree: function (oldRoot, newRoot) {
  1447.       var allElements = this.DATASource.GetAllResources();
  1448.       while (allElements.hasMoreElements()) {
  1449.          var aResource = allElements.getNext();
  1450.          if ((aResource instanceof tmRDFResource) && (aResource.Value.indexOf(oldRoot) == 0)) {
  1451.             var newNodeLabel = aResource.Value.replace(oldRoot, newRoot);
  1452.             this.copyNode(aResource, this.RDFService.GetResource(newNodeLabel), oldRoot, newRoot);
  1453.          }
  1454.       }
  1455.    },
  1456.  
  1457.    replaceStartupPref: function(result, newPath) {
  1458.       var sessionpath = !newPath ? "--" : SessionPref.getCharPref("onStart.sessionpath");
  1459.       SessionPref.setIntPref("onStart.loadsession", result.value);
  1460.       if (result.value > -1) {
  1461.          if (result.label == sessionpath ) SessionPref.setCharPref("onStart.sessionpath", newPath);
  1462.          else SessionPref.setCharPref("onStart.sessionpath", result.label);
  1463.       }
  1464.       nsIPrefServiceObj.savePrefFile(null); // store the pref immediately
  1465.    },
  1466.  
  1467.    sessionUtil: function(action, what) {
  1468.       // action = save , replace
  1469.       // type = thiswindow , allwindows
  1470.       if (!this.isValidtoSave()) return;
  1471.       if (numberOfWindows() == 1) what = "thiswindow";
  1472.       var oldPath = "", name, saveClosedTabs;
  1473.       var id = this.getAnonymousId();
  1474.       var newPath = gRDFRoot + "/saved/" + id + "/window";
  1475.       if (action == "save") {
  1476.          // ask the user for new name or for exist name if the user want to replace
  1477.          var session = this.getSessionName("save"+what);
  1478.          if (session.button == BUTTON_CANCEL) return; // user cancel
  1479.          else if (session.button == BUTTON_EXTRA1) oldPath = session.path;
  1480.          name = session.name;
  1481.          saveClosedTabs = session.saveClosedTabs;
  1482.       } else {
  1483.          oldPath = document.popupNode.session;
  1484.          name = this.getLiteralValue(oldPath, "name");
  1485.          saveClosedTabs = this.saveClosedtabs;
  1486.       }
  1487.       if (oldPath != "") { // oldPath is "" if we save to a new name
  1488.          // check if the user want to replace startup session
  1489.          var result = this.promptReplaceStartup("replaceSession", oldPath);
  1490.          if (result.button == BUTTON_CANCEL) return; // user cancel
  1491.          else if (result.button == BUTTON_OK) this.replaceStartupPref(result, newPath);
  1492.       }
  1493.       var count = this.saveOneOrAll("save"+what, newPath, saveClosedTabs);
  1494.       if (count) this.insertSession(count, name, newPath, oldPath);
  1495.       else tmLog("Error in " + action + " " + what);
  1496.    },
  1497.  
  1498.    isValidtoSave: function() {
  1499.       if ( !this.enableManager ) return false;
  1500.       var bundle_session = document.getElementById("bundle_session_manager");
  1501.       if (gBrowser.isBlankWindow()) {
  1502.          var title = bundle_session.getString("sm.title");
  1503.          var msg = bundle_session.getString("sm.dontSaveBlank.msg");
  1504.          var buttons = ["", this.setLabel("sm.button.continue")].join("\n");
  1505.          TM_PromptService([BUTTON_CANCEL, HIDE_MENUANDTEXT, HIDE_CHECKBOX],[title, msg, "", "", buttons]);
  1506.          return false;
  1507.       }
  1508.       return true;
  1509.    },
  1510.  
  1511.    saveOneOrAll: function(action, path, saveClosedTabs) {
  1512.       var numTabs, numWindows;
  1513.       switch ( action ) {
  1514.          case "savethiswindow":
  1515.                numTabs = this.saveOneWindow(path, "", false, saveClosedTabs);
  1516.                numWindows = 1;
  1517.             break;
  1518.          case "saveallwindows":
  1519.                var didSaved = this.saveAllWindows(path, "", saveClosedTabs);
  1520.                numTabs = didSaved.tab;
  1521.                numWindows = didSaved.win;
  1522.             break;
  1523.          default: return false;
  1524.       }
  1525.       if (numTabs > 0) return {win: numWindows, tab: numTabs};
  1526.       bundle_session = document.getElementById("bundle_session_manager");
  1527.       alert(bundle_session.getString("sm.sessoinSave.error"));
  1528.       return false;
  1529.    },
  1530.  
  1531.    insertSession: function SM_insertSession(count, name, path, oldPath) {
  1532.       var container = this.initContainer(gRDFRoot + "/windows");
  1533.       var index = 0;
  1534.       if (oldPath != "") index = container.IndexOf(this.RDFService.GetResource(oldPath));
  1535.       var node = this.RDFService.GetResource(path);
  1536.       container.InsertElementAt(node, index+1, true);
  1537.       if (oldPath != "") { // remove the session we replace
  1538.          container.RemoveElementAt(index, true);
  1539.          this.removeSession(oldPath, gRDFRoot+'/windows');
  1540.       }
  1541.       this.setLiteral(node, "name", name);
  1542.       this.setLiteral(node, "nameExt", this.getNameData(count.win, count.tab));
  1543.       this.saveStateDelayed();
  1544.       return true;
  1545.    },
  1546.  
  1547.    getSessionName: function(action, old) {
  1548.       var bundle_session = document.getElementById("bundle_session_manager");
  1549.       var showChebox, closedtabMsg, saveClosedTabs = this.saveClosedtabs;
  1550.       if (action != "rename" && saveClosedTabs) {
  1551.          closedtabMsg = bundle_session.getString("sm.saveClosedTab.chkbox.label");
  1552.          showChebox = CHECKBOX_CHECKED;
  1553.       } else showChebox = HIDE_CHECKBOX;
  1554.       var msg = bundle_session.getString("sm.sessionName.msg0") + "\n";
  1555.       var title = bundle_session.getString("sm.sessionName.title." + action);
  1556.       var label, buttons, actionFlag;
  1557.       var sessionList = this.getSessionList("saved");
  1558.       if (action=="rename") {
  1559.          label = old;
  1560.          buttons = [this.setLabel("sm.sessionName.button0"),
  1561.                         this.setLabel("sm.sessionName.button1")].join("\n");
  1562.          actionFlag = DLG_RENAME;
  1563.       } else {
  1564.          label = action == "saveprevious" ? old : gBrowser.mCurrentTab.label;
  1565.          buttons = [this.setLabel("sm.askBeforSave.button0"),
  1566.                         this.setLabel("sm.askBeforSave.button1"),
  1567.                         this.setLabel("sm.replaceStartup.button0")+"..."].join("\n");
  1568.          actionFlag = DLG_SAVE;
  1569.          for (var i = 0; i < sessionList.list.length; i++) {
  1570.             if (label == sessionList.list[i]) {
  1571.                label = "";
  1572.                break;
  1573.             }
  1574.          }
  1575.       }
  1576.       label = unescape(label + "\n" + sessionList.list.join("\n"));
  1577.       var result = TM_PromptService([BUTTON_OK, SHOW_TEXTBOX, showChebox, actionFlag],[title, msg, label, closedtabMsg, buttons]);
  1578.       switch (result.button) {
  1579.          case BUTTON_CANCEL: return {button: result.button};
  1580.          case BUTTON_OK:
  1581.          case BUTTON_EXTRA1 :
  1582.             var trimResult = result.label.replace(/^[\s]+/g,"").replace(/[\s]+$/g,"");
  1583.             return {button: result.button, name: escape(trimResult), path: sessionList.path[result.value], saveClosedTabs: result.checked};
  1584.       }
  1585.       return {};
  1586.    },
  1587.  
  1588.    countWinsAndTabs: function SM_countWinsAndTabs(container, prop) {
  1589.       // count windows and tabs in this session
  1590.       var numTabs = 0, numWindows = 0;
  1591.       var windowEnum = container.GetElements();
  1592.       while (windowEnum.hasMoreElements()) {
  1593.          var rdfNodeWindow = windowEnum.getNext();
  1594.          if (prop && this.nodeHasArc(rdfNodeWindow, prop))
  1595.             continue;
  1596.          numWindows += 1;
  1597.          var rdfNodeTabs = this.getResource(rdfNodeWindow, "tabs");
  1598.          if (rdfNodeTabs instanceof tmRDFResource) {
  1599.             var tabContainer = this.initContainer(rdfNodeTabs);
  1600.             numTabs += tabContainer.GetCount();
  1601.          }
  1602.       }
  1603.       return {win: numWindows, tab: numTabs};
  1604.    },
  1605.  
  1606.    getNameData: function(numWindows, numTabs) {
  1607.       var d = new Date();
  1608.       var date = [d.getFullYear(), '/', d.getMonth()<9 ? "0":"", d.getMonth()+1, '/', d.getDate()<10 ? "0":"", d.getDate()].join('');
  1609.       var time = [d.getHours()<10 ? "0":"", d.getHours(), ':', d.getMinutes()<10 ? "0":"", d.getMinutes(), ':', d.getSeconds()<10 ? "0":"", d.getSeconds()].join('');
  1610.       var bundle_session = document.getElementById("bundle_session_manager");
  1611.       var empty = bundle_session.getString("sm.session.empty");
  1612.       var T = bundle_session.getString("sm.session.tabs");
  1613.       var W = bundle_session.getString("sm.session.windows");
  1614.       if (numWindows == 0) return ", (" + empty + ") (" + date + " " + time + ")";
  1615.       else if (numWindows < 2) return ", (" + numTabs + " "+ T + ") (" + date + " " + time + ")";
  1616.       return ", (" + numWindows + " " + W + ", " + numTabs + " " + T + ") (" + date + " " + time + ")";
  1617.    },
  1618.  
  1619.    updateSessionMenu: function(menu) {
  1620.       if (typeof(document.popupNode.session) == "undefined")
  1621.         return false;
  1622.  
  1623.       var overwriteWindows = SessionPref.getBoolPref("restore.overwritewindows") || gSingleWindowMode;
  1624.       document.getElementById("tm-sm-OpenInCurrenWindow").setAttribute("default",overwriteWindows);
  1625.       document.getElementById("tm-sm-OpenInNewWindow").setAttribute("default",!overwriteWindows);
  1626.       document.getElementById("tm-sm-OpenInNewWindow").hidden = gSingleWindowMode;
  1627.  
  1628.       var mValue = document.popupNode.getAttribute("value");
  1629.       if (mValue <= -1)
  1630.          document.getElementById("tm-sm-Rename").setAttribute("disabled",true);
  1631.       else
  1632.          document.getElementById("tm-sm-Rename").removeAttribute("disabled");
  1633.  
  1634.       var node = document.popupNode.parentNode.parentNode;
  1635.       var mItem = document.getElementById("tm-sm-SetAsStartup");
  1636.       if (node.id == "tm-sessionmanager" || node.id == "btn_sessionmanager") {
  1637.          mItem.removeAttribute("disabled");
  1638.          if (document.popupNode.hasAttribute("default"))
  1639.             mItem.setAttribute("checked", "true");
  1640.         else
  1641.             mItem.removeAttribute("checked");
  1642.       } else {
  1643.          mItem.removeAttribute("checked");
  1644.          mItem.setAttribute("disabled",true);
  1645.       }
  1646.  
  1647.       var mShowext = document.getElementById("tm-sm-showext");
  1648.       var showext = SessionPref.getBoolPref("menu.showext");
  1649.       if (!showext && mShowext.hasAttribute("checked"))
  1650.          mShowext.removeAttribute("checked");
  1651.       else if (showext && !mShowext.hasAttribute("checked"))
  1652.          mShowext.setAttribute("checked", "true");
  1653.  
  1654.       var obsAll = document.getElementById("tmp_contextmenu_AllWindows");
  1655.       var obsThis = document.getElementById("tmp_contextmenu_ThisWindow");
  1656.       var mSave = document.getElementById("tm-sm-Save");
  1657.       if (node.id.indexOf("tm-sm-closedwindows")==0 || node.id == "btn_closedwindows" || mValue <= -1) {
  1658.          if (obsAll.hidden != true)
  1659.             obsAll.hidden = true;
  1660.          if (obsThis.hidden != true)
  1661.             obsThis.hidden = true;
  1662.          if (mSave.hidden != false)
  1663.             mSave.hidden = false;
  1664.          if (document.popupNode.hasAttribute("disabled"))
  1665.             mSave.setAttribute("disabled", true);
  1666.          else
  1667.             mSave.removeAttribute("disabled");
  1668.       } else {
  1669.          var isOneWindow = (numberOfWindows() == 1);
  1670.          if (obsAll.hidden != isOneWindow)
  1671.             obsAll.hidden = isOneWindow;
  1672.          if (obsThis.hidden != false)
  1673.             obsThis.hidden = false;
  1674.          if (mSave.hidden != true)
  1675.             mSave.hidden = true;
  1676.       }
  1677.       return true;
  1678.    },
  1679.  
  1680.    restoreSession: function(node, overwriteWindows) {
  1681.       if (!this.enableManager)
  1682.          return;
  1683.       // call restoreSession after delay to let the popup menu time to hide
  1684.       window.setTimeout( function () { SessionManager.delayRestoreSession(node, overwriteWindows); }, 0 );
  1685.    },
  1686.  
  1687.    delayRestoreSession: function(node, overwriteWindows) {
  1688.       var path = node.session;
  1689.       var id = node.parentNode.parentNode.id;
  1690.       if (id == "tm-sessionmanager" || id == "btn_sessionmanager")
  1691.          this.loadSession(path,'sessionrestore', overwriteWindows);
  1692.       else if (id.indexOf("tm-sm-closedwindows")==0 || id == "btn_closedwindows")
  1693.          this.openclosedwindow(path, overwriteWindows);
  1694.    },
  1695.  
  1696.    setSessionAsStartup: function(popup) {
  1697.       if (popup.getAttribute("checked")) {
  1698.          var aValue = document.popupNode.getAttribute("value"); // -1, -2 for for closed session, 1,2.... for saved session
  1699.          var loadsession = aValue && aValue <= -1 ? aValue : 0;
  1700.          SessionPref.setIntPref("onStart.loadsession", loadsession);
  1701.          if (loadsession > -1)
  1702.             SessionPref.setCharPref("onStart.sessionpath", document.popupNode.session);
  1703.          nsIPrefServiceObj.savePrefFile(null); // store the pref immediately
  1704.       }
  1705.    },
  1706.  
  1707.    setShowNameExt: function() {
  1708.       SessionPref.setBoolPref("menu.showext", !SessionPref.getBoolPref("menu.showext"));
  1709.       nsIPrefServiceObj.savePrefFile(null); // store the pref immediately
  1710.    },
  1711.  
  1712.    renameSession: function SM_renameSession(thisSession) {
  1713.       var node = this.RDFService.GetResource(thisSession);
  1714.       var oldName = this.getLiteralValue(node, "name");
  1715.       var result = this.getSessionName("rename", oldName);
  1716.       if (result.button == BUTTON_OK) {
  1717.          this.setLiteral(node, "name", result.name);
  1718.          this.saveStateDelayed();
  1719.       }
  1720.    },
  1721.  
  1722.    removeFromMenu: function(event, popup, root) {
  1723.       if (!tabxPrefs.getBoolPref("middleclickDelete")) return;
  1724.       if ( event.button == 1 && ("session" in event.target)) {
  1725.          SessionManager.removeSavedSession(event.target);
  1726.          if (root == gSessionPath[0] && this.isClosedWindowsEmpty()) popup.hidePopup();
  1727.          else SessionManager.createMenu(popup, root);
  1728.       }
  1729.    },
  1730.  
  1731.    removeSavedSession: function(aMenuItem, aRemoveSession) {
  1732.       var node = aMenuItem.parentNode.parentNode;
  1733.       var path = aMenuItem.session;
  1734.       if (aRemoveSession || node.id == "tm-sessionmanager" || node.id == "btn_sessionmanager") {
  1735.          // before we remove this session check if it is the startup session
  1736.          // and let the user cancel the delete or choose diffrent startup session
  1737.          var result = this.promptReplaceStartup("removeSavedSession", path);
  1738.          switch (result.button) {
  1739.             case BUTTON_CANCEL: return;
  1740.             case BUTTON_OK: this.replaceStartupPref(result, "");
  1741.             case NO_NEED_TO_REPLACE : this.removeSession(path, gRDFRoot+'/windows');
  1742.          }
  1743.       } else if (node.id.indexOf("tm-sm-closedwindows")==0 || node.id == "btn_closedwindows") {
  1744.          this.removeSession(path, gSessionPath[0]);
  1745.          this.updateClosedWindowsMenu("check");
  1746.       }
  1747.    },
  1748.  
  1749.    removeAllSavedSession: function SM_removeAllSavedSession() {
  1750.       var node = document.popupNode.parentNode.parentNode;
  1751.       var result, title, msg;
  1752.       var bundle_session = document.getElementById("bundle_session_manager");
  1753.       var buttons = [this.setLabel("sm.removeStartup.button0"),
  1754.                      this.setLabel("sm.removeStartup.button1")].join("\n");
  1755.       if (node.id == "tm-sessionmanager" || node.id == "btn_sessionmanager") {
  1756.          title = bundle_session.getString("sm.removeAll.title.session");
  1757.          msg = bundle_session.getString("sm.removeAll.msg0") + "\n\n";
  1758.          if (SessionPref.getIntPref("onStart.loadsession") > -1)
  1759.             msg += bundle_session.getString("sm.removeAll.msg1");
  1760.          result = TM_PromptService([BUTTON_CANCEL, HIDE_MENUANDTEXT, HIDE_CHECKBOX],
  1761.                         [title, msg, "", "", buttons]);
  1762.          if (result.button == BUTTON_OK) {
  1763.             this.deleteSubtree(gSessionPath[1]);
  1764.             this.deleteSubtree(gSessionPath[2]);
  1765.             this.deleteSubtree(gSessionPath[3]);
  1766.             this.deleteSubtree(gRDFRoot+'/saved');
  1767.             this.deleteSubtree(gRDFRoot+'/windows');
  1768.             this.saveStateDelayed();
  1769.             SessionPref.setIntPref("onStart.loadsession", -1);
  1770.             nsIPrefServiceObj.savePrefFile(null); // store the pref immediately
  1771.          }
  1772.       } else if (node.id.indexOf("tm-sm-closedwindows")==0 || node.id == "btn_closedwindows") {
  1773.          title = bundle_session.getString("sm.removeAll.title.closedwindow");
  1774.          msg = bundle_session.getString("sm.removeAll.msg2");
  1775.          result = TM_PromptService([BUTTON_CANCEL, HIDE_MENUANDTEXT, HIDE_CHECKBOX],
  1776.                         [title, msg, "", "", buttons]);
  1777.          if (result.button == BUTTON_OK) {
  1778.             var sessionContainer = this.initContainer(gSessionPath[0]);
  1779.             this.deleteWithProp(sessionContainer, "status", "saved");
  1780.             this.updateClosedWindowsMenu(true);
  1781.             this.saveStateDelayed();
  1782.          }
  1783.       }
  1784.    },
  1785.  
  1786.    // xxx need to check if we need all this functions
  1787.    removeSession: function SM_removeSession(value, container) {
  1788.       if (value==null) return;
  1789.       var node = this.RDFService.GetResource(value);
  1790.       var rdfNodeWindows = this.RDFService.GetResource(container);
  1791.       var windowsContainer = this.initContainer(rdfNodeWindows);
  1792.       this.deleteSubtree(value);
  1793.       windowsContainer.RemoveElement(node, true);
  1794.       if (!windowsContainer.GetCount()) this.deleteNode(rdfNodeWindows);
  1795.       this.saveStateDelayed();
  1796.    },
  1797.  
  1798.    removeAllClosedSession: function SM_removeAllClosedSession() {
  1799.       for (var i = 0; i < gSessionPath.length; i++) {
  1800.          var rdfNode = this.RDFService.GetResource(gSessionPath[i]);
  1801.          var container = this.initContainer(rdfNode);
  1802.          if (!this.containerEmpty(gSessionPath[i])) this.deleteWithProp(container);
  1803.          if (!container.GetCount()) this.deleteNode(rdfNode);
  1804.       }
  1805.    },
  1806.  
  1807.    deleteSession: function SM_deleteSession(nodLabel, prop, value) {
  1808.       var rdfNode = this.RDFService.GetResource(nodLabel);
  1809.       var container = this.initContainer(rdfNode);
  1810.       if (!this.containerEmpty(nodLabel)) this.deleteWithProp(container, prop, value);
  1811.       if (!container.GetCount()) this.deleteNode(rdfNode);
  1812.    },
  1813.  
  1814.    deleteWithProp: function(container, prop, value) {
  1815.       var containerEnum = container.GetElements();
  1816.       var nodeToDelete = [];
  1817.       var noProp = typeof(prop) == "undefined";
  1818.       var valueExist = typeof(value) == "string";
  1819.       while(containerEnum.hasMoreElements()) {
  1820.          var node = containerEnum.getNext();
  1821.          var propExist = noProp ? true : this.nodeHasArc(node, prop);
  1822.          if (valueExist && !noProp && propExist && this.getLiteralValue(node, prop) != value) propExist = false;
  1823.          if (propExist) nodeToDelete.push(node);
  1824.       }
  1825.       this.deleteArrayNodes(container, nodeToDelete, true);
  1826.    },
  1827.  
  1828.    deleteArrayNodes: function(container, nodeToDelete, deleteSubTree) {
  1829.       for (var i = 0; i < nodeToDelete.length; i++) {
  1830.          var nodeValue = nodeToDelete[i].QueryInterface(tmRDFResource).Value;
  1831.          if (deleteSubTree) this.deleteSubtree(nodeValue);
  1832.          container.RemoveElement(nodeToDelete[i], true);
  1833.       }
  1834.    },
  1835.  
  1836.    destroyMenuItems: function(menu, aRemoveAllItems) {
  1837.       // Destroy the items.
  1838.       var destroy = aRemoveAllItems || false, endSeparator;
  1839.       for (var i = 0; i < menu.childNodes.length; i++) {
  1840.          var item = menu.childNodes[i];
  1841.          if (item.id.indexOf("-endSeparator") != -1) {
  1842.             endSeparator = item
  1843.             if (menu.parentNode.id != "tm-sessionmanager" &&
  1844.                 menu.parentNode.getAttribute("anonid") != "delete" &&
  1845.                 menu.parentNode.id != "btn_sessionmanager")
  1846.                break;
  1847.             else
  1848.                continue;
  1849.          }
  1850.          if (destroy) {
  1851.             i--;
  1852.             menu.removeChild(item);
  1853.          } else if (item.id.indexOf("-startSeparator") != -1) destroy = true;
  1854.       }
  1855.       return endSeparator;
  1856.    },
  1857.  
  1858.    createMenuForDialog: function(popup, contents) {
  1859.       if (contents == SHOW_CLOSED_WINDOW_LIST) {
  1860.          // create closed window list popup menu
  1861.          this.createMenu(popup, window.opener.gSessionPath[0], contents);
  1862.       } else {
  1863.          // create saved Session popup menu
  1864.          this.createMenu(popup, gRDFRoot+'/windows', contents);
  1865.          // check if sessionpath and loadsessions valid for saved session
  1866.          var loadsession = SessionPref.getIntPref("onStart.loadsession");
  1867.          if (loadsession > -1 && contents != 1 && loadsession != popup.parentNode.sessionIndex) {
  1868.             SessionPref.setIntPref("onStart.loadsession", popup.parentNode.sessionIndex);
  1869.             var pref = "onStart.sessionpath";
  1870.             if (popup.parentNode.sessionIndex < 0 && SessionPref.prefHasUserValue(pref))
  1871.                SessionPref.clearUserPref(pref);
  1872.          }
  1873.       }
  1874.    },
  1875.  
  1876.    createMenu: function SM_createMenu(popup, container, contents, aNoSeparators) {
  1877.       if (popup.id == "btn_closedwindows_menu" && gIsFirefox35) {
  1878.         var contextmenu = !this.enableManager ? "tm_undocloseWindowContextMenu" : "tm_sessionmanagerContextMenu";
  1879.         document.getElementById("btn_closedwindows_menu").setAttribute("contextmenu", contextmenu);
  1880.         if (!this.enableManager) {
  1881.           HistoryMenu.populateUndoWindowSubmenu("btn_closedwindows_menu");
  1882.           return;
  1883.         }
  1884.       }
  1885.  
  1886.       if (!this.DATASource) this.initService(); // initService if we call from pref dialog
  1887.       if (typeof(contents) == "undefined") contents = 0;
  1888.       var endSeparator = this.destroyMenuItems(popup, aNoSeparators); // Remove any existing menu items
  1889.       var parentId = popup.parentNode.id;
  1890.       if (parentId == "btn_sessionmanager" || parentId == "btn_closedwindows")
  1891.          popup.parentNode.removeAttribute("tooltiptext");
  1892.       var parentID;
  1893.       if (popup.parentNode.getAttribute("anonid") == "delete")
  1894.          parentID = "tm_prompt";
  1895.       else if (contents != SHOW_CLOSED_WINDOW_LIST)
  1896.          parentID = popup.parentNode.id;
  1897.       var aContainer = this.initContainer(container);
  1898.       var containerEnum = aContainer.GetElements();
  1899.       var mi, node, name, nameExt, accessKey,index, nodes = [];
  1900.       var showNameExt = SessionPref.getBoolPref("menu.showext");
  1901.       var loadsession = SessionPref.getIntPref("onStart.loadsession");
  1902.       var sessionpath = SessionPref.getCharPref("onStart.sessionpath");
  1903.       var showTooltip = parentId == "btn_sessionmanager" || parentId == "tm-sessionmanager" || parentId.indexOf("tm-sm-closedwindows")==0 || parentId == "btn_closedwindows";
  1904.       var closedWinList = parentId.indexOf("closedwindows") != -1;
  1905.       while(containerEnum.hasMoreElements()) {
  1906.          node = containerEnum.getNext();
  1907.          if (this.nodeHasArc(node, "status") &&
  1908.                this.getLiteralValue(node, "status") != "saved") continue;
  1909.          nodes.push(node);
  1910.       }
  1911.       var count = nodes.length;
  1912.       for (var i = 0; i < count; i++) {
  1913.          node = nodes[i];
  1914.          name = unescape(this.getLiteralValue(node, "name"));
  1915.          nameExt = this.getLiteralValue(node, "nameExt");
  1916.          // Insert a menu item for session in the container
  1917.          mi = document.createElement("menuitem");
  1918.          mi.session = node.QueryInterface(tmRDFResource).Value;
  1919.          mi.setAttribute("session", mi.session);
  1920.          if (contents == 1 && loadsession > -1 && mi.session && mi.session == sessionpath) continue;
  1921.          mi.setAttribute("value", i);
  1922.          mi.value = i;
  1923.          if (parentID != "onStart.loadsession") {
  1924.             index = closedWinList ? count - 1 - i : i;
  1925.             accessKey = (index > 25) ? "" : String.fromCharCode(65 + index) + "  " ;
  1926.             mi.setAttribute("accesskey", accessKey);
  1927.             mi.setAttribute("label", accessKey + name + (showNameExt ? nameExt : ""));
  1928.             if (showTooltip) mi.setAttribute("tooltiptext", accessKey + name + nameExt);
  1929.          } else {
  1930.             mi.setAttribute("label", name);
  1931.          }
  1932.          popup.insertBefore(mi, closedWinList ? popup.childNodes[1] : endSeparator);
  1933.       }
  1934.       var allEmpty = true;
  1935.       switch ( parentID ) {
  1936.          case "tm-sessionmanager":
  1937.          case "btn_sessionmanager":
  1938.             var observer = document.getElementById("tmp_menu_AllWindows");
  1939.             var isOneWindow = (numberOfWindows() == 1);
  1940.             if (observer.hidden != isOneWindow) observer.hidden = isOneWindow;
  1941.          case "tm_prompt":
  1942.             endSeparator.hidden = endSeparator.previousSibling.localName == "menuseparator";
  1943.             var sessionLabel;
  1944.             var afterCrash = !this.containerEmpty(gSessionPath[3]);
  1945.             // if Crashed is empty don't show 'Crashed Session' menu item
  1946.             if (afterCrash && contents != 1)
  1947.                sessionLabel = ["lastgood 1","previous 2","crashed 3"];
  1948.             else
  1949.                sessionLabel = ["last 1","previous 2"];
  1950.  
  1951.             var menu;
  1952.             var bundle_session = document.getElementById("bundle_session_manager");
  1953.             var empty = ", (" + bundle_session.getString("sm.session.empty") + ")";
  1954.             for (i = 0; i < sessionLabel.length; i++ ){
  1955.                menu = document.createElement("menuitem");
  1956.                var [sLabel , sessionIndex] = sessionLabel[i].split(" ");
  1957.                menu.session = gSessionPath[sessionIndex];
  1958.                if (this.containerEmpty(menu.session) && contents != 1)
  1959.                   menu.setAttribute("disabled", "true");
  1960.                else
  1961.                   allEmpty = false;
  1962.                nameExt = this.getLiteralValue(menu.session, "nameExt", empty);
  1963.                sLabel = bundle_session.getString("sm.sessionMenu." + sLabel);
  1964.                menu.setAttribute("label", sLabel + (showNameExt && contents != 1 ? nameExt : ""));
  1965.                if (showTooltip) menu.setAttribute("tooltiptext", sLabel + nameExt);
  1966.                menu.setAttribute("value", (-1 - i));
  1967.                popup.appendChild (menu);
  1968.             }
  1969.             if (afterCrash && contents != 1) { // add separator before Crashed menu item
  1970.                menu = document.createElement("menuseparator");
  1971.                popup.insertBefore(menu, popup.lastChild);
  1972.             }
  1973.             if (contents == 1) loadsession = -1; //set "Last Sessoin" as default in the list
  1974.             this.setDefaultIndex(popup, loadsession, sessionpath);
  1975.             break;
  1976.          case "onStart.loadsession":
  1977.             endSeparator.hidden = this.containerEmpty(container);
  1978.             this.setDefaultIndex(popup, loadsession, sessionpath);
  1979.             break;
  1980.          default:
  1981.             this.setDefaultIndex(popup, loadsession, sessionpath);
  1982.             if (endSeparator)
  1983.                endSeparator.hidden = true;
  1984.             break;
  1985.       }
  1986.       var rename = popup.getElementsByAttribute("anonid", "rename")[0];
  1987.       if (rename)
  1988.          TMP_setItem(rename, "disabled", count == 0 ? true : null);
  1989.       var deleteItem = popup.getElementsByAttribute("anonid", "delete")[0];
  1990.       if (deleteItem)
  1991.          TMP_setItem(deleteItem, "disabled", allEmpty && count == 0 ? true : null);
  1992.    },
  1993.  
  1994.    // set defaultIndex, sessionIndex and default Attribute
  1995.    setDefaultIndex : function(popup, loadsession, sessionpath) {
  1996.       popup.parentNode.defaultIndex = -1; // index with menuseparator
  1997.       popup.parentNode.sessionIndex = -1; // index without menuseparator
  1998.       var i, item, value, checked;
  1999.       for (i = 0; i < popup.childNodes.length; i++) {
  2000.          item = popup.childNodes[i];
  2001.          if (item.localName == "menuseparator") continue;
  2002.          value = item.getAttribute("value");
  2003.          checked = ((loadsession > -1 && item.session && item.session == sessionpath) ||
  2004.                (loadsession <= -1 && value && value == loadsession));
  2005.          if (checked) {
  2006.             item.setAttribute("default", "true");
  2007.             popup.parentNode.defaultIndex = i;
  2008.             popup.parentNode.sessionIndex = value;
  2009.          } else item.removeAttribute("default");
  2010.       }
  2011.    },
  2012.  
  2013.    // update disable/enable to closed window list in tool menu and toolbar
  2014.    updateClosedWindowsMenu: function(action) {
  2015.       var disabled = (action == "check") ? this.isClosedWindowsEmpty(): action;
  2016.       var wnd, enumerator = windowEnumerator();
  2017.       while ( enumerator.hasMoreElements() ) {
  2018.          wnd = enumerator.getNext();
  2019.          var broadcaster = wnd.document.getElementById("tmp_closedwindows");
  2020.          if (broadcaster)
  2021.             broadcaster.setAttribute("disabled",disabled);
  2022.       }
  2023.    },
  2024.  
  2025.    isClosedWindowsEmpty: function SM_isClosedWindowsEmpty() {
  2026.       var node, disabled = true;
  2027.       var aContainer = this.initContainer(gSessionPath[0]);
  2028.       var containerEnum = aContainer.GetElements();
  2029.       while(containerEnum.hasMoreElements()) {
  2030.          node = containerEnum.getNext();
  2031.          if (this.getLiteralValue(node, "status") == "saved") {
  2032.             disabled = false;
  2033.             break;
  2034.          }
  2035.       }
  2036.       return disabled;
  2037.    },
  2038.  
  2039.    // call by init on first window load after crash
  2040.    openAfterCrash: function SM_openAfterCrash(status) {
  2041.       var sessionContainer = this.initContainer(gSessionPath[0]);
  2042.       if (this.enableBackup) {
  2043.          var path = gRDFRoot + "/closedSession/thisSession";
  2044.          this.setLiteral(path, "status", "crash2");
  2045.          // restore to were we was before the crash
  2046.          var crashedContainer = this.initContainer(gSessionPath[3]);
  2047.          if (status != "crash2") {
  2048.             // delete old crash data
  2049.             if (!this.containerEmpty(gSessionPath[3])) this.deleteWithProp(crashedContainer);
  2050.             var windowEnum = sessionContainer.GetElements();
  2051.             var nodeToDelete = [];
  2052.             while (windowEnum.hasMoreElements()) {
  2053.                var rdfNodeWindow = windowEnum.getNext();
  2054.                if (this.getLiteralValue(rdfNodeWindow, "status") == "backup") {
  2055.                   var tabs = this.getResource(rdfNodeWindow, "tabs");
  2056.                   var subTree = rdfNodeWindow.QueryInterface(tmRDFResource).Value;
  2057.                   if (!this.containerEmpty(tabs)) { // copy "backup" subtree to crashed session if it not empty
  2058.                      var newSubTree = subTree.replace(gSessionPath[0], gSessionPath[3]);
  2059.                      this.copySubtree(subTree, newSubTree);
  2060.                      crashedContainer.AppendElement(this.RDFService.GetResource(newSubTree));
  2061.                   }
  2062.                   this.deleteSubtree(subTree); // delete the crashed subtree
  2063.                   nodeToDelete.push(rdfNodeWindow);// remove the window from the crash session
  2064.                } else this.setLiteral(rdfNodeWindow, "dontLoad", "true"); // we can see this session in the close window list
  2065.             }
  2066.             this.deleteArrayNodes(sessionContainer, nodeToDelete, false);
  2067.          } // if firefox was crashed in middle of crash Recovery try again to restore the same data
  2068.          else if (!this.containerEmpty(gSessionPath[0]))
  2069.             this.deleteWithProp(sessionContainer);
  2070.  
  2071.          var bundle_session = document.getElementById("bundle_session_manager");
  2072.          var title = bundle_session.getString("sm.afterCrash.title");
  2073.          var msg;
  2074.          if (status != "crash2")
  2075.             msg = bundle_session.getString("sm.afterCrash.msg0");
  2076.          else
  2077.             msg = bundle_session.getString("sm.afterCrash.msg0.again");
  2078.          var chkBoxLabel = !this.enableManager ? bundle_session.getString("sm.afterCrash.chkbox.label") : "";
  2079.          var buttons, result, whattoLoad = "session";
  2080.          var chkBoxState = !this.enableManager ? CHECKBOX_UNCHECKED : HIDE_CHECKBOX;
  2081.          var closedWinList = this.initContainer(gSessionPath[0]).GetCount();
  2082.          var lastSession = this.containerEmpty(gSessionPath[1]) // last session
  2083.          var prevtoLast = this.containerEmpty(gSessionPath[2]) // previous to last
  2084.          var savedSession = this.containerEmpty(gRDFRoot+'/windows') // saved session
  2085.          var isAllEmpty = lastSession && prevtoLast && savedSession;
  2086.          if (!this.containerEmpty(gSessionPath[3])) { // if Crashed Session is not empty
  2087.             var count = this.countWinsAndTabs(crashedContainer);
  2088.             this.setLiteral(gSessionPath[3], "nameExt", this.getNameData(count.win, count.tab));
  2089.             if (this.enableManager && !isAllEmpty) {
  2090.                msg += "\n\n" + bundle_session.getString("sm.afterCrash.msg1");
  2091.                buttons = [this.setLabel("sm.afterCrash.button0"),
  2092.                           this.setLabel("sm.afterCrash.button1")].join("\n");
  2093.                result = TM_PromptService([BUTTON_OK, SHOW_MENULIST, HIDE_CHECKBOX, SELECT_CRASH],
  2094.                               [title, msg, "", "", buttons]);
  2095.             } else {
  2096.                msg += " " + bundle_session.getString("sm.afterCrash.msg2") + ".....";
  2097.                if (!this.enableManager)
  2098.                   msg += "\n" + bundle_session.getString("sm.afterCrash.msg3");
  2099.                else
  2100.                   msg += "\n" + bundle_session.getString("sm.afterCrash.msg4");
  2101.                buttons = [this.setLabel("sm.afterCrash.button0.crashed"),
  2102.                           this.setLabel("sm.afterCrash.button1")].join("\n");
  2103.                result = TM_PromptService([BUTTON_OK, HIDE_MENUANDTEXT, chkBoxState],
  2104.                               [title, msg, "", chkBoxLabel, buttons]);
  2105.                result.label = gSessionPath[3];
  2106.             }
  2107.          } else {
  2108.             if (this.enableManager && !isAllEmpty) {
  2109.                msg += " " + bundle_session.getString("sm.afterCrash.msg5") + "\n\n"
  2110.                           + bundle_session.getString("sm.afterCrash.msg1");
  2111.                buttons = [this.setLabel("sm.afterCrash.button0"),
  2112.                           this.setLabel("sm.afterCrash.button1")].join("\n");
  2113.                result = TM_PromptService([BUTTON_OK, SHOW_MENULIST, HIDE_CHECKBOX, SELECT_DEFAULT],
  2114.                               [title, msg, "", "", buttons]);
  2115.             } else if (closedWinList != 0) {
  2116.                msg += " " + bundle_session.getString("sm.afterCrash.msg6");
  2117.                if (!this.enableManager)
  2118.                   msg += "\n" + bundle_session.getString("sm.afterCrash.msg3") + "\n\n"
  2119.                               + bundle_session.getString("sm.afterCrash.msg7") + ":";
  2120.                else
  2121.                   msg += "\n\n" + bundle_session.getString("sm.afterCrash.msg7") + " "
  2122.                                 + bundle_session.getString("sm.afterCrash.msg8") + ":";
  2123.                buttons = [this.setLabel("sm.afterCrash.button0"),
  2124.                           this.setLabel("sm.afterCrash.button1")].join("\n");
  2125.                result = TM_PromptService([BUTTON_OK, SHOW_MENULIST, chkBoxState, SHOW_CLOSED_WINDOW_LIST],
  2126.                               [title, msg, "", chkBoxLabel, buttons]);
  2127.                whattoLoad = "closedwindow";
  2128.             } else {// nothing to restore
  2129.                msg = bundle_session.getString("sm.afterCrash.msg9") + "\n" + bundle_session.getString("sm.afterCrash.msg10");
  2130.                if (!this.enableManager)
  2131.                   msg += "\n\n" + bundle_session.getString("sm.afterCrash.msg3");
  2132.                buttons = ["", this.setLabel("sm.button.continue")].join("\n");
  2133.                result = TM_PromptService([BUTTON_CANCEL, HIDE_MENUANDTEXT, chkBoxState],
  2134.                               [title, msg, "", chkBoxLabel, buttons]);
  2135.             }
  2136.          }
  2137.          if (result.checked && !this.enableManager) {
  2138.             SessionPref.setBoolPref("manager", true); // enable session manager
  2139.             try {
  2140.                nsIPrefServiceObj.savePrefFile(null); // store the pref immediately
  2141.             } catch(ex) { }
  2142.          }
  2143.          if (result.button == BUTTON_OK) {
  2144.             switch ( whattoLoad ) {
  2145.                case "session": this.loadSession(result.label, "firstwindowopen");
  2146.                   break;
  2147.                case "closedwindow": this.openclosedwindow(result.label, true);
  2148.                   break;
  2149.                default:
  2150.             }
  2151.          } else
  2152.             this.loadHomePage();
  2153.       } else { // crash recovery is off, delete any remains from the crashed session
  2154.          if (!this.containerEmpty(gSessionPath[0])) this.deleteWithProp(sessionContainer);
  2155.          if (this.enableManager) this.openFirstWindow(true); // openFirstWindow with flag openAfterCrash
  2156.          //  else BrowserHome(); // we never get to here...
  2157.       }
  2158.    },
  2159.  
  2160.    // call by init or by openAfterCrash on first window load
  2161.    openFirstWindow: function SM_openFirstWindow(afterCrash) {
  2162.       var path = gRDFRoot + "/closedSession/";
  2163.       var sessionType = ["thisSession", "lastSession", "previoustolastSession", "crashedsession"];
  2164.       // swap 0 --> 1 --> 2 --> 0
  2165.       var i;
  2166.       var sessions = [], subTree, aSession;
  2167.       for (i = 0; i < sessionType.length-1; i++) {
  2168.          sessions.push(this.getResource(path + sessionType[i], "session"));
  2169.       }
  2170.       for (i = 0; i < sessionType.length-1; i++) {
  2171.          if (i == 0) { // delete oldest session subtree
  2172.             aSession = sessions[sessionType.length-2];
  2173.             subTree = aSession.QueryInterface(tmRDFResource).Value;
  2174.             this.deleteSubtree(subTree);
  2175.          } else aSession = sessions[i-1];
  2176.          this.setResource(path + sessionType[i], "session", aSession)
  2177.       }
  2178.       for (i = 0; i < sessionType.length; i++) {
  2179.          gSessionPath[i] = this.getResourceValue(path + sessionType[i], "session");
  2180.       }
  2181.       gThisWin = gSessionPath[0] + "/" + gBrowser.windowID;
  2182.       gThisWinTabs = gThisWin + "/tabs";
  2183.       gThisWinClosedtabs = gThisWin + "/closedtabs";
  2184.       // When Firefox Starts:
  2185.       //       pref "onStart"
  2186.       //       0 - Restore
  2187.       //       1 - Ask me before Restore
  2188.       //       2 (or else) - Don't Restore
  2189.       //
  2190.       //       pref "onStart.loadsession"
  2191.       //       0 , 1 , 2 ..... index of saved sessions
  2192.       //       -1, -2 ........ index of previous sessions
  2193.       //
  2194.       // if loadsession >= 0 the session path is saved in pref "onStart.sessionpath"
  2195.       // else if loadsession < 0 the session path is saved in gSessionPath
  2196.       var restoreFlag = SessionPref.getIntPref("onStart");
  2197.       if (restoreFlag > 1) {
  2198.          return; // Don't Restore
  2199.       }
  2200.       var loadSession = SessionPref.getIntPref("onStart.loadsession");
  2201.       // after last session end with restart load the last session without any prompt
  2202.       // unless we are after crash
  2203.       var startupEmpty = false, savePref = false ;
  2204.       var result = {}, title, msg = "", buttons;
  2205.       var bundle_session = document.getElementById("bundle_session_manager");
  2206.       if (afterCrash)
  2207.          title = bundle_session.getString("sm.afterCrash.title");
  2208.       else
  2209.          title = bundle_session.getString("sm.start.title");
  2210.       var chkBoxLabel = afterCrash ? bundle_session.getString("sm.start.chkbox.label") : "";
  2211.       var chkBoxState = afterCrash ? CHECKBOX_UNCHECKED : HIDE_CHECKBOX;
  2212.       // get saved session list
  2213.       var sessionList = this.getSessionList();
  2214.       var askifempty = restoreFlag > 1 ? false : SessionPref.getBoolPref("onStart.askifempty");
  2215.       if (sessionList == null) {
  2216.          if (((askifempty && afterCrash) || restoreFlag == 1) && !this.corruptedFile) {
  2217.             msg = bundle_session.getString("sm.start.msg0") + "\n"
  2218.                 + bundle_session.getString("sm.afterCrash.msg10");
  2219.             if (afterCrash)
  2220.                msg += "\n\n" + bundle_session.getString("sm.start.msg1");
  2221.             buttons = ["", this.setLabel("sm.button.continue")].join("\n");
  2222.             result = TM_PromptService([BUTTON_CANCEL, HIDE_MENUANDTEXT, chkBoxState],
  2223.                            [title, msg, "", chkBoxLabel, buttons]);
  2224.             if (result.checked && afterCrash) {
  2225.                SessionPref.setBoolPref("crashRecovery", true); // enable Crash Recovery
  2226.                nsIPrefServiceObj.savePrefFile(null); // store the pref immediately
  2227.             }
  2228.          }
  2229.          this.loadHomePage();
  2230.          return;
  2231.       }
  2232.  
  2233.       var aList = sessionList.list;
  2234.       var sessionPath = sessionList.path;
  2235.       var loadSessionIsValid = true, sessionIndex, thisPath;
  2236.       switch ( (loadSession > 0) ? 0 : loadSession ) {
  2237.          case 0:
  2238.             sessionIndex = null;
  2239.             if (SessionPref.prefHasUserValue("onStart.sessionpath")) {
  2240.                thisPath = SessionPref.getCharPref("onStart.sessionpath");
  2241.                // check if sessionpath is valid
  2242.                for (i = 0; i < aList.length; i++) {
  2243.                   if (sessionPath[i] == thisPath) {
  2244.                      sessionIndex = i;
  2245.                      break;
  2246.                   }
  2247.                }
  2248.             }
  2249.             if ((thisPath && this.containerEmpty(thisPath)) || sessionIndex == null) {
  2250.                // error in pref.js or in session.rdf ask the user what to do
  2251.                loadSessionIsValid = false;
  2252.                thisPath = gSessionPath[1]; // load last session
  2253.                SessionPref.setIntPref("onStart.loadsession", -1);
  2254.                savePref = true;
  2255.             }
  2256.             break;
  2257.          default: // just in case that somehow onStart.loadsession is invalid
  2258.             loadSession = -1;
  2259.             SessionPref.setIntPref("onStart.loadsession", -1);
  2260.             savePref = true;
  2261.          case -2:
  2262.          case -1:
  2263.             var indx = -1 * loadSession;
  2264.             thisPath = gSessionPath[indx];
  2265.             if (this.containerEmpty(gSessionPath[indx])) startupEmpty = true;
  2266.             sessionIndex = aList.length + indx - 3;
  2267.             break;
  2268.       }
  2269.       if (restoreFlag > 0 || afterCrash || (startupEmpty && askifempty) || !loadSessionIsValid) {
  2270. try{
  2271.          if (afterCrash)
  2272.             msg += bundle_session.getString("sm.afterCrash.msg0") + " "
  2273.                  + bundle_session.getString("sm.start.msg1");
  2274.          if (startupEmpty) msg += bundle_session.getString("sm.start.msg0");
  2275.          if (!loadSessionIsValid) msg += bundle_session.getString("sm.start.msg2");
  2276.          msg += "\n\n" + bundle_session.getString("sm.afterCrash.msg1");
  2277.          buttons = [this.setLabel("sm.afterCrash.button0"),
  2278.                     this.setLabel("sm.afterCrash.button1")].join("\n");
  2279.          result = TM_PromptService([BUTTON_OK, SHOW_MENULIST, chkBoxState, SELECT_DEFAULT],
  2280.                         [title, msg, "", chkBoxLabel, buttons]);
  2281.          if (result.checked && afterCrash) {
  2282.             SessionPref.setBoolPref("crashRecovery", true); // enable Crash Recovery
  2283.             savePref = true;
  2284.          }
  2285. } catch (ex) {TMP_ASSERT(ex);}
  2286.       }
  2287.       else {
  2288.          result.button = startupEmpty ? BUTTON_CANCEL : BUTTON_OK;
  2289.          result.label = thisPath;
  2290.       }
  2291.       if (savePref) nsIPrefServiceObj.savePrefFile(null); // store the pref immediately
  2292.       if (result.button == BUTTON_OK)
  2293.          this.loadSession(result.label, "firstwindowopen");
  2294.       else
  2295.          this.loadHomePage();
  2296.    },
  2297.  
  2298.    getSessionList: function SM_getSessionList(flag) {
  2299.       var aList = [], sessionPath = [];
  2300.       var aContainer = this.initContainer(gRDFRoot+'/windows');
  2301.       var containerEnum = aContainer.GetElements();
  2302.       var node, aName;
  2303.       while(containerEnum.hasMoreElements()) {
  2304.          node = containerEnum.getNext();
  2305.          aName = this.getLiteralValue(node, "name");
  2306.          aList.push(aName);
  2307.          sessionPath.push(node.QueryInterface(tmRDFResource).Value);
  2308.       }
  2309.       var bundle_session = document.getElementById("bundle_session_manager");
  2310.       if (flag == "saved") return {list: aList, path: sessionPath};
  2311.       else if (flag == "replace") {
  2312.          aList.push(bundle_session.getString("sm.sessionMenu.lastDefault"));
  2313.          aList.push(bundle_session.getString("sm.sessionMenu.previous"));
  2314.       } else {
  2315.          var empty = ", (" + bundle_session.getString("sm.session.empty") + ")";
  2316.          var empty1 = this.containerEmpty(gSessionPath[1]);
  2317.          var empty2 = this.containerEmpty(gSessionPath[2]);
  2318.          if (empty1 && empty2 && aList.length == 0) return null;
  2319.          if (flag == "afterCrash") aList.push(bundle_session.getString("sm.sessionMenu.lastgood") + (empty1 ? empty : ""));
  2320.          else aList.push(bundle_session.getString("sm.sessionMenu.last") + (empty1 ? empty : ""));
  2321.          aList.push(bundle_session.getString("sm.sessionMenu.previous") + (empty2 ? empty : ""));
  2322.       }
  2323.       sessionPath.push(gSessionPath[1]);
  2324.       sessionPath.push(gSessionPath[2]);
  2325.       return {list: aList, path: sessionPath};
  2326.    },
  2327.  
  2328.    saveAllWindows: function SM_saveAllWindows(path, caller, saveClosedTabs) {
  2329.       var enumerator = windowEnumerator();
  2330.       var wnd, savedTabs = 0 , savedWin = 0, thisWin;
  2331.       while ( enumerator.hasMoreElements() ) {
  2332.          wnd = enumerator.getNext();
  2333.          thisWin = wnd.SessionManager.saveOneWindow(path, caller, false, saveClosedTabs);
  2334.          savedTabs += thisWin;
  2335.          if (thisWin > 0) savedWin += 1;
  2336.       }
  2337.       return {win: savedWin, tab: savedTabs};
  2338.    },
  2339.  
  2340.    /*
  2341.     *  update closed window list flag 'dontLoad'
  2342.     *  all window that where closed more then 10 sec ago will mark 'dontLoad'
  2343.     *  return true if we leftout with windows to load
  2344.     */
  2345.    updateClosedWindowList: function SM_updateClosedWindowList(aPopUp) {
  2346.       var thisSession = this.RDFService.GetResource(gSessionPath[0]);
  2347.       var container = this.initContainer(thisSession);
  2348.       var curTime;
  2349.       if (aPopUp)
  2350.          curTime = this.getLiteralValue(thisSession, "timestamp", 0);
  2351.       else {
  2352.          var pref = "extensions.tabmix.warnAboutClosingTabs.timeout";
  2353.          var delay = gTabmixPrefs.prefHasUserValue(pref) ? gTabmixPrefs.getCharPref(pref)*1 : 0;
  2354.          curTime = new Date().valueOf() - delay;
  2355.       }
  2356.       var windowEnum = container.GetElements();
  2357.       while (windowEnum.hasMoreElements()) {
  2358.          var rdfNodeLastWindow = windowEnum.getNext();
  2359.          var lastSaved = this.getLiteralValue(rdfNodeLastWindow, "timestamp", 0);
  2360.          if ((curTime - lastSaved) > 10000) {
  2361.             if (!this.nodeHasArc(rdfNodeLastWindow, "dontLoad"))
  2362.                this.setLiteral(rdfNodeLastWindow, "dontLoad", "true");
  2363.          }
  2364.       }
  2365.       var count = this.countWinsAndTabs(container,  "dontLoad");
  2366.       return count.win > 0 && count.tab > 0;
  2367.    },
  2368.  
  2369.    saveOneWindow: function SM_saveOneWindow(path, caller, overwriteWindow, saveClosedTabs) {
  2370.       if (gBrowser.isBlankWindow()) return 0; // dont save window without any tab
  2371.       if (!path) path = gSessionPath[0];
  2372.       if (!caller) caller = "";
  2373.       if (!overwriteWindow) overwriteWindow = false;
  2374.       if (typeof(saveClosedTabs) == "undefined") saveClosedTabs = this.saveClosedtabs;
  2375.       // if we going to delete close window from the list we can't use GetCount as ID,
  2376.       // we need to save unink ID
  2377.       var winID;
  2378.       if (caller == "windowclosed" || caller == "windowbackup") winID = gBrowser.windowID;
  2379.       else winID = this.getAnonymousId();
  2380.       var winPath = path + "/" + winID;
  2381.       this.initSession(path, winPath);
  2382.       var savedTabs = this.saveAllTab(winPath, 0);
  2383.       if (caller == "windowclosed" && this.enableBackup) {
  2384.          this.setTabsScroll();
  2385.       } else {
  2386.          if (((gThisWin == winPath && !this.enableBackup) || gThisWin != winPath) && saveClosedTabs)
  2387.             this.copyClosedTabsToRDF(winPath);
  2388.       }
  2389.  
  2390.       var rdfNodeThisWindow = this.RDFService.GetResource(winPath);
  2391.       if (SessionPref.getBoolPref("save.selectedtab")) // save selected tab index
  2392.          this.setIntLiteral(rdfNodeThisWindow, "selectedIndex", this.getTabPosition());
  2393.  
  2394.       if (caller == "windowbackup") {
  2395.          return savedTabs;
  2396.       }
  2397.  
  2398.       if (path == gSessionPath[0]) {
  2399.          // save current tab title. we will use it later in closed windows list as menu entry label
  2400.          // if current tab is blank get label from first saved tab that isn't blank
  2401.          var _tab = gBrowser.mCurrentTab;
  2402.          if (gBrowser.isBlankTab(_tab)) {
  2403.             for (var i = 0; i < gBrowser.mTabs.length; i++) {
  2404.                var aTab = gBrowser.mTabs[i];
  2405.                if (!gBrowser.isBlankTab(aTab)) {
  2406.                   _tab = aTab;
  2407.                   break;
  2408.                }
  2409.             }
  2410.          }
  2411.          var label = _tab.label;
  2412.          // replace "Loading..." with the document title (with minimal side-effects)
  2413.          if (label == gBrowser.mStringBundle.getString("tabs.loading")) {
  2414.             gBrowser.setTabTitle(_tab);
  2415.             [label, _tab.label] = [_tab.label, label];
  2416.          }
  2417.          this.setLiteral(rdfNodeThisWindow, "name", escape(label));
  2418.          this.setLiteral(rdfNodeThisWindow, "nameExt", this.getNameData(-1, savedTabs));
  2419.          var pref = "extensions.tabmix.warnAboutClosingTabs.timeout";
  2420.          var delay = gTabmixPrefs.prefHasUserValue(pref) ? gTabmixPrefs.getCharPref(pref)*1 : 0;
  2421.          var newTime = new Date().valueOf() - delay;
  2422.          this.setLiteral(rdfNodeThisWindow, "timestamp", newTime);
  2423.          // if we overwrite window we don't load it again on restart
  2424.          if (this.overwriteWindow || overwriteWindow)
  2425.             this.setLiteral(rdfNodeThisWindow, "dontLoad", "true");
  2426.          // when we save on close we set this in windowIsClosing
  2427.          if (caller != "windowclosed") {
  2428.             this.setLiteral(rdfNodeThisWindow, "status", "saved");
  2429.             this.updateClosedWindowsMenu(false);
  2430.          }
  2431.       }
  2432.       else
  2433.         this.setLiteral(rdfNodeThisWindow, "status", "");
  2434.       this.saveStateDelayed();
  2435.       return savedTabs;
  2436.    }, // end of "saveOneWindow : function ()"
  2437.  
  2438.    // if this session is not in the container add it to the last place and init prop
  2439.    // else move it to the last place
  2440.    initSession: function SM_initSession(path, winPath) {
  2441.       var container = this.initContainer(path);
  2442.       var rdfNode = this.RDFService.GetResource(winPath);
  2443.       var index = container.IndexOf(rdfNode);
  2444.       if (index == -1) {
  2445.          container.AppendElement(rdfNode);
  2446.          this.setLiteral(rdfNode, "status", "backup");
  2447.          this.setResource(rdfNode, "tabs", winPath + "/tabs");
  2448.          this.setResource(rdfNode, "closedtabs", winPath + "/closedtabs");
  2449.       } else if (index != container.GetCount()) {
  2450.          container.RemoveElementAt(index, true);
  2451.          container.AppendElement(rdfNode);
  2452.       }
  2453.    },
  2454.  
  2455.    // xxx need to fix this to save only history, image and history index
  2456.    // and save the rest when tab added
  2457.    tabLoaded: function SM_tabLoaded(aTab) {
  2458.       if (!this.enableBackup || aTab.hasAttribute("inrestore")) return;
  2459.       if (gBrowser.isBlankTab(aTab)) return;
  2460.       // if this window is not in the container add it to the last place
  2461.       this.initSession(gSessionPath[0], gThisWin);
  2462.       var tabContainer = this.initContainer(gThisWinTabs);
  2463.       var result = this.saveTab(aTab, gThisWinTabs, tabContainer, true, 0);
  2464.       if (result)
  2465.          this.saveStateDelayed(-1);
  2466.    },
  2467.  
  2468.    updateTabPos: function(aTab, label, add0_1) {
  2469.       var tab, node;
  2470.       if (!add0_1) add0_1 = 0;
  2471.       for (var i = aTab._tPos + add0_1; i < gBrowser.mTabs.length; i++) {
  2472.          tab = gBrowser.mTabs[i];
  2473.          node = (typeof(label) == "undefined") ? this.getNodeForTab(tab) : label + "/" + tab.linkedPanel;
  2474.          this.setIntLiteral(node, "tabPos", tab._tPos);
  2475.       }
  2476.    },
  2477.  
  2478.    tabClosed: function SM_tabClosed(aTab) { // delete tab from container and save to closed tab list backup
  2479.       // we don't check aTab.hasAttribute("inrestore") , in case tab is closed befor
  2480.       // its finish to restore.
  2481.       if (!this.enableBackup) return;
  2482.       this.initSession(gSessionPath[0], gThisWin);
  2483.       var tabContainer = this.initContainer(gThisWinTabs);
  2484.       var panelPath = this.getNodeForTab(aTab);
  2485.       var nodeToClose = this.RDFService.GetResource(panelPath);
  2486.       this.updateTabPos(aTab); // update _tPos for the tab right to the deleted tab
  2487.       if (this.saveClosedtabs) {
  2488.          // move closedtabs to closedtabs container
  2489.          var closedTabContainer = this.initContainer(gThisWinClosedtabs);
  2490.          var tabExist = true;
  2491.          if (tabContainer.IndexOf(nodeToClose) == -1) {
  2492.             tabExist = this.saveTab(aTab, gThisWinTabs, closedTabContainer, false, 0);
  2493.          } else tabContainer.RemoveElement(nodeToClose, true);
  2494.          if (tabExist) {
  2495.             closedTabContainer.AppendElement(nodeToClose);
  2496.             if (closedTabContainer.GetCount() > gTabmixPrefs.getIntPref("browser.sessionstore.max_tabs_undo"))
  2497.                this.deleteClosedtabAt(1, gThisWin);
  2498.          }
  2499.       } else if (tabContainer.IndexOf(nodeToClose) > -1) {
  2500. // xxx  try to use deleteSession ????
  2501.          this.deleteSubtree(panelPath);
  2502.          tabContainer.RemoveElement(nodeToClose, true);
  2503. // xxx is it necessary to delete this ???
  2504.          if (!tabContainer.GetCount()) { // if no tab in the container remove it from the tree
  2505.             var winContainer = this.initContainer(gSessionPath[0]);
  2506.             var rdfNode = this.RDFService.GetResource(gThisWin);
  2507.             winContainer.RemoveElement(rdfNode, true);
  2508.             this.deleteSubtree(gThisWin);
  2509.          }
  2510.       }
  2511.       this.saveStateDelayed(-1);
  2512.    },
  2513.  
  2514.    updateTabProp: function SM_updateTabProp(aTab) {
  2515.       // we dont need this function to run before sessionmanager init
  2516.       // this.enableBackup=null so we dont past the next if
  2517.       if (!this.enableBackup || aTab.hasAttribute("inrestore")) return;
  2518.       if (gBrowser.isBlankTab(aTab)) return; // dont write blank tab to the file
  2519.       this.initSession(gSessionPath[0], gThisWin);
  2520.       this.setLiteral(this.getNodeForTab(aTab), "properties", SessionData.getTabProperties(aTab, true));
  2521.       this.saveStateDelayed();
  2522.    },
  2523.  
  2524.    tabMoved: function SM_tabMoved(aTab, oldPos, newPos) {
  2525.       if (!this.enableBackup || aTab.hasAttribute("inrestore")) return;
  2526.       this.initSession(gSessionPath[0], gThisWin);
  2527.       // can't use aTab._tPos after group of tab delete
  2528.       // we pass old position and new position from TMmoveTabTo
  2529.       // we need to fix tabPos for all tab between old position and new position
  2530.       var first = Math.min(oldPos, newPos);
  2531.       var last = Math.max(oldPos, newPos);
  2532.       for (var i = first; i < last + 1; i++) {
  2533.          var tab = gBrowser.mTabs[i];
  2534.          if (!gBrowser.isBlankTab(tab))
  2535.             this.setIntLiteral(this.getNodeForTab(tab), "tabPos", i);
  2536.       }
  2537.       this.saveStateDelayed();
  2538.    },
  2539.  
  2540.    setTabsScroll: function() {
  2541.       if (SessionPref.getBoolPref("save.scrollposition"))
  2542.          for (var i = 0; i < gBrowser.mTabs.length; i++)
  2543.             this.tabScrolled(gBrowser.mTabs[i]);
  2544.    },
  2545.  
  2546.    // xxx need to find the right event to trigger this function..
  2547.    tabScrolled: function SM_tabScrolled(aTab) {
  2548.       if (!this.enableBackup || aTab.hasAttribute("inrestore")) return;
  2549.       var aBrowser = gBrowser.getBrowserForTab(aTab);
  2550.       if (gBrowser.isBlankBrowser(aBrowser)) return;
  2551.       var bContent = aBrowser.contentWindow;
  2552.       var zoomFactor = aBrowser.docShell.contentViewer ? aBrowser.markupDocumentViewer.textZoom : 1;
  2553.       this.setLiteral(this.getNodeForTab(aTab), "scroll", bContent.scrollX + "," + bContent.scrollY + "," + zoomFactor);
  2554.    },
  2555.  
  2556.    tabSelected: function(needFlush) {
  2557.       if (!this.enableBackup || gBrowser.mCurrentTab.hasAttribute("inrestore")) return;
  2558.       if (typeof(needFlush) == "undefined") needFlush = false;
  2559.       this.initSession(gSessionPath[0], gThisWin);
  2560.       this.setTabsScroll(); // until i find proper event to update tab scroll do it from here
  2561.       if (SessionPref.getBoolPref("save.selectedtab")) {
  2562.          this.setIntLiteral(gThisWin, "selectedIndex", this.getTabPosition());
  2563.       }
  2564.       if (needFlush)
  2565.          this.saveStateDelayed();
  2566.    },
  2567.  
  2568.    getTabPosition: function() { // calc selected tab position if blank tab not restore
  2569.       if (gBrowser.isBlankTab(gBrowser.mCurrentTab)) return 0; // if the current tab is blank we don't resore the index
  2570.       var blankTab = 0;
  2571.       for (var i = 0; i < gBrowser.mCurrentTab._tPos; i++) {
  2572.          if (gBrowser.isBlankTab(gBrowser.mTabs[i])) blankTab++;
  2573.       }
  2574.       return gBrowser.mCurrentTab._tPos - blankTab;
  2575.    },
  2576.  
  2577.    getNodeForTab: function(aTab) {
  2578.       return gThisWinTabs + "/" + aTab.linkedPanel;
  2579.    },
  2580.  
  2581.    saveAllTab: function SM_saveAllTab(winPath, offset, saveBusy) {
  2582.       var savedTabs = 0 ;
  2583.       var rdfNodeTabs = this.getResource(winPath, "tabs");
  2584.       var rdfLabelTabs = rdfNodeTabs.QueryInterface(tmRDFResource).Value;
  2585.       var tabContainer = this.initContainer(rdfNodeTabs);
  2586.       for (var i = 0; i < gBrowser.mTabs.length; i++) {
  2587.          var aTab = gBrowser.mTabs[i];
  2588.          if (saveBusy && !aTab.hasAttribute("busy")) continue; // save only busy tabs
  2589.          if (this.saveTab(aTab, rdfLabelTabs, tabContainer, true, offset)) savedTabs ++;
  2590.       }
  2591.       return savedTabs;
  2592.    },
  2593.  
  2594.    // call from tabloaded, tabClosed, saveAllTab
  2595. // xxx add flag what to save : all, history, property, scrollPosition
  2596.    saveTab: function SM_saveTab(aTab, rdfLabelTabs, tabContainer, needToAppend, offset) {
  2597.       var aBrowser = gBrowser.getBrowserForTab(aTab);
  2598.       if (gBrowser.isBlankBrowser(aBrowser)) return false;
  2599.  
  2600.       // clear sanitized flag
  2601.       if (SessionPref.prefHasUserValue("sanitized")) {
  2602.          SessionPref.clearUserPref("sanitized");
  2603.          this.setLiteral(gRDFRoot + "/closedSession/thisSession", "status", "crash");
  2604.       }
  2605.  
  2606.       var sessionHistory = aBrowser.webNavigation.sessionHistory;
  2607.       var rdfLabelTab = rdfLabelTabs + "/" + aTab.linkedPanel;
  2608.       var index = sessionHistory.index < 0 ? 0 : sessionHistory.index;
  2609.       var bContent = aBrowser.contentWindow;
  2610.       var zoomFactor = aBrowser.docShell.contentViewer ? aBrowser.markupDocumentViewer.textZoom : 1;
  2611.       try {
  2612.          var curHistory = sessionHistory.getEntryAtIndex(index, false);
  2613.          curHistory.QueryInterface(tmSHEntry).setScrollPosition(bContent.scrollX, bContent.scrollY);
  2614.       } catch (e) {TMP_ASSERT(ex, "saveTab error at index " + sessionHistory.index);}
  2615.       var rdfNodeTab = this.RDFService.GetResource(rdfLabelTab);
  2616.       var data = {
  2617.          index: this.enableSaveHistory ? index : 0,
  2618.          pos: aTab._tPos + offset,
  2619.          image: aTab.getAttribute("image"),
  2620.          properties: SessionData.getTabProperties(aTab, true),
  2621.          history: this.saveTabHistory(sessionHistory),
  2622.          scroll: bContent.scrollX + "," + bContent.scrollY + "," + zoomFactor
  2623.       };
  2624.       this.saveTabData(rdfNodeTab, data);
  2625.       // dont append if we call from tabClosed function
  2626. //XXX move this to the caller function
  2627.       if (tabContainer.IndexOf(rdfNodeTab) == -1 && needToAppend) {
  2628.          tabContainer.AppendElement(rdfNodeTab);
  2629.          this.updateTabPos(aTab, rdfLabelTabs, 1); // update _tPos for the tab right to the new tab
  2630.       }
  2631.       return true;
  2632.    },
  2633.  
  2634.    saveTabData: function SM_saveTabData(aNode, aData) {
  2635.       this.setIntLiteral(aNode, "index",      aData.index);
  2636.       this.setIntLiteral(aNode, "tabPos",     aData.pos);
  2637.       this.setLiteral   (aNode, "image",      aData.image || ""); // for use in closed tab list
  2638.       this.setLiteral   (aNode, "properties", aData.properties);
  2639.       this.setLiteral   (aNode, "history",    aData.history);
  2640.       this.setLiteral   (aNode, "scroll",     aData.scroll);
  2641.    },
  2642.  
  2643.    // xxx save text size (zoom), char type ?
  2644.    saveTabHistory: function(sessionHistory) {
  2645.       var historyStart = this.enableSaveHistory ? 0 : sessionHistory.index;
  2646.       var historyEnd = this.enableSaveHistory ? sessionHistory.count : sessionHistory.index+1;
  2647.       var j, historyEntry, history = [];
  2648.       for (j = historyStart; j < historyEnd; j++) {
  2649.          try {
  2650.             historyEntry = sessionHistory.getEntryAtIndex(j, false).QueryInterface(tmSHEntry);
  2651.             history.push(escape(historyEntry.title));
  2652.             history.push(historyEntry.URI.spec);
  2653.             history.push(this.getScrollPosHs(historyEntry)); // not in use yet
  2654.          } catch (ex) {TMP_ASSERT(ex, "saveTabHistory error at index " + j); }
  2655.       }
  2656.       // generate unique separator and combine the array to one string
  2657.       var separator = "][", extraSeparator = "@";
  2658.       for (var i = 0; i < history.length; ++i) {
  2659.          while (history[i].indexOf(separator) > -1)
  2660.             separator += extraSeparator;
  2661.       }
  2662.       // insert the separator to history so we can extract it in loadTabHistory
  2663.       return separator + "|-|" + history.join(separator);
  2664.    },
  2665.  
  2666.    getScrollPosHs: function(historyEntry) {
  2667.       if (SessionPref.getBoolPref("save.scrollposition")) {
  2668.          var x={}, y={};
  2669.          historyEntry.getScrollPosition(x, y);
  2670.          return x.value + "," + y.value;
  2671.       } return "0,0";
  2672.    },
  2673.  
  2674.    loadSession: function SM_loadSession(path, caller, overwriteWindows) {
  2675.       var sessionContainer = this.initContainer(path);
  2676.       var sessionEnum = sessionContainer.GetElements();
  2677.       var sessionCount = 0, concatenate;
  2678.       var windowEnum = windowEnumerator();
  2679.       if (typeof(overwriteWindows) == "undefined")
  2680.          overwriteWindows = SessionPref.getBoolPref("restore.overwritewindows");
  2681.       // don't concatenate window after crash
  2682.       if (caller == "firstwindowopen" && this.getLiteralValue(gSessionPath[0], "status") == "crash2")
  2683.          concatenate = false;
  2684.       else
  2685.          concatenate = SessionPref.getBoolPref("restore.concatenate");
  2686.       var saveBeforOverwrite = SessionPref.getBoolPref("restore.saveoverwrite");
  2687.       var overwriteTabs = SessionPref.getBoolPref("restore.overwritetabs");
  2688.  
  2689.       // in single window mode we restore ALL window into this window
  2690.       if (gSingleWindowMode)
  2691.          concatenate = true;
  2692.  
  2693.       // if this window is blank use it when reload session
  2694.       var wnd, blankWindow;
  2695.       if (!gSingleWindowMode && concatenate && !overwriteWindows && !gBrowser.isBlankWindow() && caller != "firstwindowopen" && caller != "concatenatewindows") {
  2696.          this.openNewWindow(path, "concatenatewindows");
  2697.          return;
  2698.       }
  2699.       // if we join all window to one window
  2700.       // call the same window for all saved window with overwritewindows=false and overwritetabs=false if this not the first saved
  2701.       // for first saved window overwritetabs determined by user pref
  2702.       while (sessionEnum.hasMoreElements()) {
  2703.          sessionCount++;
  2704.          var rdfNodeSession = sessionEnum.getNext();
  2705.          if (rdfNodeSession instanceof tmRDFResource) {
  2706.             var windowPath = rdfNodeSession.QueryInterface(tmRDFResource).Value;
  2707.             if (this.nodeHasArc(windowPath, "dontLoad")) continue;
  2708.             if (concatenate) {
  2709.                if (caller != "concatenatewindows" && caller != "firstwindowopen" && sessionCount == 1
  2710.                          && saveBeforOverwrite && overwriteTabs)
  2711.                   this.saveOneWindow(gSessionPath[0], "", true);
  2712.                var newCaller = (sessionCount != 1) ? caller+"-concatenate" : caller;
  2713.                this.loadOneWindow(windowPath, newCaller);
  2714.             } else {
  2715.                wnd = null;
  2716.                blankWindow = false;
  2717.                if (windowEnum.hasMoreElements()) {
  2718.                   wnd = windowEnum.getNext();
  2719.                   blankWindow = wnd.gBrowser.isBlankWindow();
  2720.                }
  2721.                if (wnd != null && (overwriteWindows || blankWindow || (caller == "firstwindowopen" && sessionCount == 1 ))) {
  2722.                   // if we save overwrite windows in the closed windows list don't forget to set dontLoad==true
  2723.                   if (caller != "firstwindowopen" && saveBeforOverwrite && overwriteTabs)
  2724.                      wnd.SessionManager.saveOneWindow(gSessionPath[0], "", true);
  2725.                   wnd.SessionManager.loadOneWindow(windowPath, caller);
  2726.                } else
  2727.                   this.openNewWindow(windowPath, caller);
  2728.             }
  2729.          }
  2730.       }
  2731.       // cloes extra windows if we overwrite open windows and set dontLoad==true
  2732.       if (numberOfWindows() > 1 && overwriteWindows) {
  2733.          while (windowEnum.hasMoreElements()) {
  2734.             wnd = windowEnum.getNext();
  2735.             if (concatenate && wnd == window) continue;
  2736.             if (saveBeforOverwrite) wnd.SessionManager.overwriteWindow = true;
  2737.             else wnd.SessionManager.saveThisWindow = false;
  2738.             wnd.close();
  2739.          }
  2740.       }
  2741.    },
  2742.  
  2743.    openclosedwindow: function SM_openclosedwindow(path, overwriteWindows) {
  2744.       // 1. check if to overwrite the opener window
  2745.       //    if 1 is true call loadOneWindow
  2746.       //    if 1 is false open new window and pass the path
  2747.       // 2. delete the window from closedwindow list (after new window is opend and load)
  2748.       var rdfNodeClosedWindow = this.RDFService.GetResource(path);
  2749.       // don't reopen same window again. the window removed from closed window list after it finish to load
  2750.       if (this.nodeHasArc(rdfNodeClosedWindow, "reOpened")) return;
  2751.       this.setLiteral(rdfNodeClosedWindow, "reOpened", "true");
  2752.       if (typeof(overwriteWindows) == "undefined") overwriteWindows = SessionPref.getBoolPref("restore.overwritewindows");
  2753.       var saveBeforOverwrite = SessionPref.getBoolPref("restore.saveoverwrite");
  2754.       var overwriteTabs = SessionPref.getBoolPref("restore.overwritetabs");
  2755.       if (overwriteWindows || gBrowser.isBlankWindow() || gSingleWindowMode) {
  2756.          if (saveBeforOverwrite && overwriteTabs)
  2757.             this.saveOneWindow(gSessionPath[0], "", true);
  2758.          this.loadOneWindow(path, "openclosedwindow");
  2759.       } else
  2760.          this.openNewWindow(path, "openclosedwindow");
  2761.  
  2762.       this.saveStateDelayed();
  2763.    },
  2764.  
  2765.    openNewWindow: function SM_openNewWindow(path, caller) {
  2766.       var newWindow = window.openDialog( getBrowserURL(), "_blank", "chrome,all,dialog=no", null);
  2767.       newWindow.tabmixdata = { path: path, caller: caller };
  2768.    },
  2769.  
  2770.    loadOneWindow: function SM_loadOneWindow(path, caller) {
  2771.       var overwrite = true, restoreSelect = SessionPref.getBoolPref("save.selectedtab");
  2772.       switch ( caller ) {
  2773.          case "firstwindowopen":
  2774.                if (window.arguments && window.arguments.length > 0) {
  2775.                   overwrite = window.arguments[0] == gHomeButton.getHomePage() ? true : false ;
  2776.                   if (!overwrite && window.arguments[0] != "about:blank") restoreSelect = false;
  2777.                } else overwrite = false;
  2778.             break;
  2779.          case "windowopenebytabmix":
  2780.          case "concatenatewindows":
  2781.                overwrite = true;
  2782.             break;
  2783.          case "openclosedwindow":
  2784.          case "sessionrestore":
  2785.             overwrite = SessionPref.getBoolPref("restore.overwritetabs");
  2786.             break;
  2787.          case "firstwindowopen-concatenate":
  2788.          case "openclosedwindow-concatenate":
  2789.          case "sessionrestore-concatenate":
  2790.          case "concatenatewindows-concatenate":
  2791.             overwrite = false;
  2792.             break;
  2793.          default: tmLog("SessionManager \n error unidentifid caller " + caller);
  2794.       }
  2795. /*
  2796.       1. when open first windows overwrite tab only if they are home page, if firefox open from link or with
  2797.          pages that are not the home page append the new tab to the end.
  2798.          simple solution is to set browser.startup.page = 0 , when we activate session manager, in this case if we
  2799.          have any tabs in the first window we don't overwrite.
  2800.       2. when open window by session manager other than the first window (caller = "windowopenebytabmix" and tabmix in the name) overwrite=true
  2801.       3. when loadOneWindow call by openclosedwindow or loadSession we reuse window check user pref for overwrite.
  2802.       4. if we open all closed windows to one window append tab to the end and select the selected tab from first window
  2803.          in the session.
  2804. */
  2805.       var cTab = gBrowser.mCurrentTab;
  2806.       var concatenate = caller.indexOf("-concatenate") != -1 || (caller == "firstwindowopen" && gBrowser.mTabContainer.childNodes.length > 1);
  2807.       var rdfNodeWindow = this.RDFService.GetResource(path);
  2808.       var rdfNodeTabs = this.getResource(rdfNodeWindow, "tabs");
  2809.       if (!(rdfNodeTabs instanceof tmRDFResource) || this.containerEmpty(rdfNodeTabs)) {
  2810.          var bundle_session = document.getElementById("bundle_session_manager");
  2811.          alert(bundle_session.getString("sm.restoreError.msg0") + "\n"  + bundle_session.getString("sm.restoreError.msg1"));
  2812.          var stringBundle = document.getElementById("tmp-string-bundle");
  2813.          var tabmix_loading = stringBundle.getString("session.loading.label") + "...";
  2814.          if (gBrowser.mCurrentTab.label == tabmix_loading)
  2815.             gBrowser.selectedBrowser.reload();
  2816.          return;
  2817.       }
  2818.       var tabContainer = this.initContainer(rdfNodeTabs);
  2819.       var newtabsCount = tabContainer.GetCount();
  2820.       gBrowser.tabsToLoad = newtabsCount;
  2821.       gBrowser.mTabContainer.nextTab = 1;
  2822.       this.setStripVisibility(newtabsCount);
  2823.       var lastSelectedIndex = restoreSelect ? this.getIntValue(rdfNodeWindow, "selectedIndex") : 0;
  2824.       if (lastSelectedIndex < 0 || lastSelectedIndex >= newtabsCount) lastSelectedIndex = 0;
  2825.       var i, newIndex, aTab, tabPos;
  2826.       if (overwrite) {
  2827.          if (gBrowser.mTabContainer.collapsedTabs > 0)
  2828.             gBrowser.mTabContainer.collapsedTabs = 0;
  2829.          for ( i = 0; i < gBrowser.mTabContainer.childNodes.length; i++) {
  2830.             aTab = gBrowser.mTabContainer.childNodes[i];
  2831.             var aBrowser = gBrowser.getBrowserForTab(aTab);
  2832.             // reset old history
  2833.             aBrowser.webNavigation.sessionHistory =
  2834.                Components.classes["@mozilla.org/browser/shistory;1"]
  2835.                .createInstance(tmSHistory);
  2836.             // remove selected and flst_id from all tabs but the current
  2837.             if (aTab != cTab) {
  2838.                aTab.removeAttribute("visited");
  2839.                aTab.removeAttribute("flst_id");
  2840.             }
  2841.             // if we need to remove extra tabs make sure they are not protected
  2842.             if (aTab.hasAttribute("protected"))
  2843.                aTab.removeAttribute("protected");
  2844.          }
  2845.          while (newtabsCount > gBrowser.mTabContainer.childNodes.length) {
  2846.             gBrowser.addTab("about:blank");
  2847.          }
  2848.          // move selected tab to place
  2849.          gBrowser.TMmoveTabTo(cTab, lastSelectedIndex);
  2850.          // remove extra tab
  2851.          while (newtabsCount < gBrowser.mTabContainer.childNodes.length) {
  2852.             gBrowser.removeTab(gBrowser.mTabContainer.lastChild);
  2853.          }
  2854.          newIndex = 0;
  2855.       } else {
  2856.          // reuse blank tabs and move tabs to the right place
  2857.          var openTabNext = gTabmixPrefs.getBoolPref("extensions.tabmix.openTabNext");
  2858.          // catch blank tab for reuse
  2859.          var blankTabs = [], blankTabsCount = 0, currentTabIsBalnk = false;
  2860.          for (i = 0; i < gBrowser.mTabContainer.childNodes.length ; i++) {
  2861.             aTab = gBrowser.mTabContainer.childNodes[i];
  2862.             if (!aTab.loadOnStartup) { // make sure we not overwrite tab that loads from apps
  2863.                if (gBrowser.isBlankTab(aTab) && (aTab.hasAttribute("tabmix_busy") || !aTab.hasAttribute("busy"))) {
  2864.                   aTab.removeAttribute("tabmix_busy");
  2865.                   if (aTab != cTab) {
  2866.                      blankTabs.push(aTab);
  2867.                      aTab.removeAttribute("visited");
  2868.                      aTab.removeAttribute("flst_id");
  2869.                   } else {
  2870.                      blankTabs.unshift(aTab);
  2871.                      currentTabIsBalnk = true;
  2872.                   }
  2873.                }
  2874.             } else delete aTab.loadOnStartup;
  2875.          }
  2876.          // remove extra tabs
  2877.          var blankTab;
  2878.          while (blankTabs.length > newtabsCount) {
  2879.             blankTab = blankTabs.pop();
  2880.             if (blankTab) gBrowser.removeTab(blankTab);
  2881.          }
  2882.          var newPos = (openTabNext && cTab._tPos < gBrowser.mTabContainer.childNodes.length - 1 && !concatenate) ? cTab._tPos + 1 : gBrowser.mTabContainer.childNodes.length - 1;
  2883.          // move blank tabs to new position
  2884.          blankTabsCount = blankTabs.length;
  2885.          while (blankTabs.length > 0) {
  2886.             blankTab = blankTabs.shift();
  2887.             tabPos = (blankTab._tPos < newPos && newPos < gBrowser.mTabContainer.childNodes.length - 1) ? newPos - 1 : newPos;
  2888.             gBrowser.TMmoveTabTo(blankTab, tabPos);
  2889.          }
  2890.          var tabsCount = gBrowser.mTabContainer.childNodes.length;
  2891.          var newTotalTabsCount = tabsCount - blankTabsCount + newtabsCount;
  2892.          while (newTotalTabsCount > gBrowser.mTabContainer.childNodes.length) {
  2893.             var newTab = gBrowser.addTab("about:blank");
  2894.             // in concatenate mode move tab to the end of the list
  2895.             if (concatenate && openTabNext) {
  2896.                gBrowser.TMmoveTabTo(newTab, gBrowser.mTabContainer.childNodes.length-1);
  2897.             }
  2898.          }
  2899.          if (tabsCount == blankTabsCount) newPos = 0;
  2900.          else newPos = (openTabNext && cTab._tPos < gBrowser.mTabContainer.childNodes.length - 1 && !concatenate) ? cTab._tPos + 1 : tabsCount - blankTabsCount;
  2901.          if (!concatenate && restoreSelect) { // in concatenate mode we select tab only from first window
  2902.             if (currentTabIsBalnk) { // if the current tab is not blank select new tab
  2903.                if (openTabNext && newPos > 0) newPos--;
  2904.                // move selected tab to place
  2905.                gBrowser.TMmoveTabTo(cTab, newPos + lastSelectedIndex);
  2906.             }
  2907.             else
  2908.               this.updateSelected(newPos + lastSelectedIndex, caller=="firstwindowopen" || caller=="windowopenebytabmix");
  2909.          }
  2910.          newIndex = newPos;
  2911.       }
  2912.       // call ensureTabIsVisible before and after we reload the tab
  2913.       gBrowser.mTabContainer.ensureTabIsVisible(gBrowser.mTabContainer.selectedIndex);
  2914.       var tabsEnum = tabContainer.GetElements();
  2915.       // sort the tab by "tabPos"
  2916.       var rdfTabs = [], rdfNodeTab;
  2917.       while (tabsEnum.hasMoreElements()) {
  2918.          rdfNodeTab = tabsEnum.getNext();
  2919.          if (rdfNodeTab instanceof tmRDFResource) {
  2920.             tabPos = this.getIntValue(rdfNodeTab, "tabPos");
  2921.             rdfTabs.push([tabPos, rdfNodeTab]);
  2922.          }
  2923.       }
  2924.       rdfTabs.sort(this.sortByColumn(0 ,true));
  2925.       this.initSession(gSessionPath[0], gThisWin); // init the new container before we start to load data
  2926.  
  2927.       // restore the selected tab first
  2928.       var selectedTabLoaded;
  2929.       if (restoreSelect && lastSelectedIndex in rdfTabs) {
  2930.          selectedTabLoaded = true;
  2931.          aTab = gBrowser.mTabContainer.childNodes[newIndex + lastSelectedIndex];
  2932.          this.loadOneTab(rdfTabs[lastSelectedIndex][1], aTab);
  2933.       }
  2934.  
  2935.       for (i = 0; i < rdfTabs.length ; i++) {
  2936.          if (selectedTabLoaded && lastSelectedIndex == i)
  2937.             continue;
  2938.          aTab = gBrowser.mTabContainer.childNodes[newIndex + i];
  2939.          this.loadOneTab(rdfTabs[i][1], aTab);
  2940.       }
  2941.  
  2942.       if (this.saveClosedtabs)
  2943.          this.saveClosedTabs(path, gThisWin, "closedtabs", true);  // load prev saved closed tabs and save to current backup
  2944.       gBrowser.mTabContainer.nextTab = 1;
  2945.       // if we open closed window delete this window from closed window list
  2946.       var caller1;
  2947.       if ("tabmixdata" in window) {
  2948.          caller1 = window.tabmixdata.caller;
  2949.          delete window.tabmixdata;
  2950.       }
  2951.       if (caller == "openclosedwindow" || caller1 == "openclosedwindow"){
  2952.          if (this.nodeHasArc(rdfNodeWindow, "reOpened")) {
  2953.             this.removeSession(path, gSessionPath[0]);
  2954.             this.updateClosedWindowsMenu("check");
  2955.          }
  2956.       }         
  2957.    },
  2958.  
  2959.    updateSelected: function(newIndex, removeAttribute) {
  2960.       let oldIndex = gBrowser.mTabContainer.selectedIndex;
  2961.       if (newIndex != oldIndex) {
  2962.         let tabs = gBrowser.mTabContainer.childNodes;      
  2963.         gBrowser.selectedTab = tabs[newIndex];
  2964.         if (removeAttribute) {
  2965.           tabs[oldIndex].removeAttribute("visited");
  2966.           tabs[oldIndex].removeAttribute("flst_id");
  2967.         }
  2968.       }
  2969.    },
  2970.  
  2971.    setStripVisibility: function(tabCount) {
  2972.       if (tabCount > 1 && gTabmixPrefs.getBoolPref("browser.tabs.autoHide") && !gBrowser.getStripVisibility()) {
  2973.         // unhide the tab bar
  2974.         gBrowser.setStripVisibilityTo(true);
  2975.         // forceHide was removed in Firefox 3.5
  2976.         if (gTabmixPrefs.prefHasUserValue("browser.tabs.forceHide"))
  2977.            gTabmixPrefs.clearUserPref("browser.tabs.forceHide")
  2978.       }
  2979.    },
  2980.  
  2981.    sortByColumn: function(nCol, bDescending) {
  2982.       var c = nCol;
  2983.       var d = bDescending;
  2984.       return function (n1, n2) {
  2985.          if (n1[c] < n2[c]) return (d) ? -1 : +1;
  2986.          if (n1[c] > n2[c]) return (d) ? +1 : -1;
  2987.          return 0;
  2988.       }
  2989.    },
  2990.  
  2991.    saveClosedTabs: function SM_saveClosedTabs(fromPath, toPath, conPath, updateClosedTabsList) {
  2992.       var isClosedTabs = conPath == "closedtabs";
  2993.       if (isClosedTabs && !(this.saveClosedtabs))
  2994.          return;
  2995.  
  2996.       var fromOld = this.wrapContainer(fromPath, conPath);
  2997.       if (!(fromOld.Root instanceof tmRDFResource)) return;
  2998.       var toNew = this.wrapContainer(toPath, conPath);
  2999.       var rdfNodeTabs = this.getResource(toPath, "tabs");
  3000.       var rdfLabelTabs = rdfNodeTabs.QueryInterface(tmRDFResource).Value;
  3001.       var maxTabsUndo = gTabmixPrefs.getIntPref("browser.sessionstore.max_tabs_undo");
  3002.       var newIndex = -1;
  3003.       while (fromOld.Enum.hasMoreElements()) {
  3004.          var rdfNodeSession = fromOld.Enum.getNext();
  3005.          if (!(rdfNodeSession instanceof tmRDFResource)) continue;
  3006.          newIndex++;
  3007.          if (isClosedTabs && (fromOld.Count - newIndex > gTabmixPrefs.getIntPref("browser.sessionstore.max_tabs_undo"))) continue;
  3008.          var uniqueId = "panel" + Date.now() + newIndex;
  3009.          var rdfLabelSession = rdfLabelTabs + "/" + uniqueId;
  3010.          var newNode = this.RDFService.GetResource(rdfLabelSession);
  3011.          var data = {}
  3012.          data.pos = this.getIntValue(rdfNodeSession, "tabPos");
  3013.          data.image = this.getLiteralValue(rdfNodeSession, "image");
  3014.          data.properties = this.getLiteralValue(rdfNodeSession, "properties");
  3015.          data.scroll = this.getLiteralValue(rdfNodeSession, "scroll"); // including zoom factor
  3016.          if (this.enableBackup) { // save only if backup enabled
  3017.             toNew.Container.AppendElement(newNode);
  3018.             data.index = this.getIntValue(rdfNodeSession, "index");
  3019.             data.history = this.getLiteralValue(rdfNodeSession, "history");
  3020.             this.saveTabData(newNode, data);
  3021.             // delete old entry if closedTabs container wasn't empty
  3022.             if (isClosedTabs && (toNew.Container.GetCount() > maxTabsUndo))
  3023.                this.deleteClosedtabAt(1, toPath);
  3024.          }
  3025.          if(updateClosedTabsList && !TMP_ClosedTabs.ssIsON) {
  3026.             var savedHistory = this.loadTabHistory(rdfNodeSession);
  3027.             data.history = savedHistory.history;
  3028.             if (savedHistory == null) {
  3029.                tmLog("closed tab at index " + newIndex + " failed to load data from the saved session");
  3030.                continue;
  3031.             }
  3032.             try {
  3033.                data.title = savedHistory.history.getEntryAtIndex(savedHistory.index, true).title;
  3034.                if (!SessionPref.getBoolPref("save.scrollposition"))
  3035.                   data.scroll = "0,0,1";
  3036.                else if (data.scroll.split(",").length < 3) // version before 0.3.0.603 don't include zoomfactor
  3037.                      data.scroll += ",1"
  3038.                gBrowser.closedTabs.unshift(data);
  3039.             } catch (ex) {TMP_ASSERT(ex, "saveClosedTabs error at index " + savedHistory.index); }
  3040.             // delete old entry if gBrowser.closedTabs wasn't empty
  3041.             var tabsCount = gBrowser.closedTabs.length;
  3042.             if (tabsCount > maxTabsUndo)
  3043.                gBrowser.closedTabs.splice(maxTabsUndo, tabsCount - maxTabsUndo);
  3044.          }
  3045.       }
  3046.       if (updateClosedTabsList) {
  3047.          // if we after restart we get closed data from FF sessionRestore
  3048.          if (TMP_ClosedTabs.ssIsON && !TMP_SessionStore.isAfterRestart()) {
  3049.             var state = { windows: [], _firstTabs: true };
  3050.             state.windows[0] = { _closedTabs: [] };
  3051.             state.windows[0]._closedTabs = convertSession.getClosedTabsState(this.getResource(fromPath, "closedtabs"));
  3052.             TMP_ClosedTabs.ss.setWindowState(window, state.toSource(), false);
  3053.          }
  3054.       }
  3055.    },
  3056.  
  3057.    copyClosedTabsToRDF: function SM_copyClosedTabsToRDF(winPath) {
  3058.       var rdfNodeTo = this.getResource(winPath, "closedtabs");
  3059.       var toContainer = this.initContainer(rdfNodeTo);
  3060.       var rdfNodeTabs = this.getResource(winPath, "tabs");
  3061.       var rdfLabelTabs = rdfNodeTabs.QueryInterface(tmRDFResource).Value;
  3062.       var ctabs = TMP_ClosedTabs.getClosedTabData;
  3063.       var tabCount = ctabs.length;
  3064.       var maxTabsUndo = gTabmixPrefs.getIntPref("browser.sessionstore.max_tabs_undo");
  3065.       var tabData, uniqueId, rdfLabelSession, newNode, historyEntry, scrollPos, history;
  3066.       for (var i = tabCount - 1; i >= 0; i--) {
  3067.          uniqueId = "panel" + Date.now() + i;
  3068.          rdfLabelSession = rdfLabelTabs + "/" + uniqueId;
  3069.          newNode = this.RDFService.GetResource(rdfLabelSession);
  3070.          toContainer.AppendElement(newNode);
  3071.          tabData = ctabs[i];
  3072.          if (TMP_ClosedTabs.ssIsON) {
  3073.             this.getSessionStoreDataForRDF(tabData);
  3074.          }
  3075.          else {
  3076.             tabData.index = tabData.history.index;
  3077.             // convert history object into string
  3078.             tabData.history = this.saveTabHistory(tabData.history);
  3079.             if (!SessionPref.getBoolPref("save.scrollposition"))
  3080.                tabData.scroll = "0,0,1";
  3081.          }
  3082.  
  3083.          this.saveTabData(newNode, tabData);
  3084.  
  3085.          // delete old entry if closedTabs container wasn't empty
  3086.          if (toContainer.GetCount() > maxTabsUndo)
  3087.             this.deleteClosedtabAt(1, winPath);
  3088.       }
  3089.       this.saveStateDelayed();
  3090.    },
  3091.  
  3092.    getSessionStoreDataForRDF: function SM_getSessionStoreDataForRDF(aTabData) {
  3093.       var tabState = aTabData.state;
  3094.       var count = tabState.entries.length;
  3095.       var activeIndex = (tabState.index || count) - 1;
  3096.       var historyStart = this.enableSaveHistory ? 0 : activeIndex;
  3097.       var historyEnd = this.enableSaveHistory ? count : activeIndex + 1;
  3098.       var j, historyEntry, history = [];
  3099.       for (j = historyStart; j < historyEnd; j++) {
  3100.          try {
  3101.             historyEntry = tabState.entries[j];
  3102.             history.push(escape(historyEntry.title));
  3103.             history.push(historyEntry.url);
  3104.             history.push(historyEntry.scroll || "0,0"); // not in use yet
  3105.          } catch (ex) {TMP_ASSERT(ex, "saveTabHistory error at index " + j); }
  3106.       }
  3107.       // generate unique separator and combine the array to one string
  3108.       var separator = "][", extraSeparator = "@";
  3109.       for (var i = 0; i < history.length; ++i) {
  3110.          while (history[i].indexOf(separator) > -1)
  3111.             separator += extraSeparator;
  3112.       }
  3113.       // insert the separator to history so we can extract it in loadTabHistory
  3114.       aTabData.history = separator + "|-|" + history.join(separator);
  3115.       aTabData.index = this.enableSaveHistory ? activeIndex : 0,
  3116.  
  3117.       aTabData.scroll = SessionPref.getBoolPref("save.scrollposition") ?
  3118.                          (tabState.entries[activeIndex].scroll || "0,0") + "," + (aTabData.zoom || 1) : "0,0,1";
  3119.       // closed tab can not be protected - set protected to 0
  3120.       var _locked = TMP_SessionStore._getAttribute(tabState, "_locked") != "false" ? "1" : "0";
  3121.       aTabData.properties = "0" + _locked;
  3122.       if ("disallow" in tabState && tabState.disallow) {
  3123.          for (var j = 0; j < SessionData.docShellItems.length; j++ )
  3124.             aTabData.properties += tabState.disallow.indexOf(SessionData.docShellItems[j]) == -1 ? "1" : "0";
  3125.       }
  3126.       else {
  3127.          aTabData.properties += "11111";
  3128.       }
  3129.       if ("attributes" in tabState && tabState.attributes) {
  3130.          delete tabState.attributes["_locked"];
  3131.          for (var name in tabState.attributes) {
  3132.             aTabData.properties += " " + name + "=" + encodeURI(tabState.attributes[name]);
  3133.          }
  3134.       }
  3135.       if ("xultab" in tabState && tabState.xultab) {
  3136.          tabState.xultab = tabState.xultab.replace(" _locked=true", "").replace(" _locked=false", "");
  3137.          if (tabState.xultab)
  3138.             aTabData.properties += " " + tabState.xultab;
  3139.       }
  3140.    },
  3141.  
  3142.    deleteAllClosedtabs: function(sessionContainer) { // delete all closed tabs in this session
  3143.       var windowEnum = sessionContainer.GetElements();
  3144.       while (windowEnum.hasMoreElements()) {
  3145.          var rdfNodeWindow = windowEnum.getNext();
  3146.          this.deleteWinClosedtabs(rdfNodeWindow.QueryInterface(tmRDFResource).Value);
  3147.       }
  3148.    },
  3149.  
  3150.    deleteWinClosedtabs: function SM_deleteWinClosedtabs(winPath) {
  3151.       var rdfNodeTabs = this.getResource(winPath, "closedtabs");
  3152.       var container = this.initContainer(rdfNodeTabs);
  3153.       this.deleteWithProp(container);
  3154.       this.saveStateDelayed();
  3155.    },
  3156.  
  3157.    deleteClosedtabAt: function SM_deleteClosedtabAt(index, winPath) {
  3158.       if (!SessionPref.getBoolPref("save.closedtabs"))
  3159.          return;
  3160.  
  3161.       if (typeof(winPath) == 'undefined')
  3162.          winPath = gThisWin;
  3163.       var rdfNodeTabs = this.getResource(winPath, "closedtabs");
  3164.       var container = this.initContainer(rdfNodeTabs);
  3165.       if (index == "last")
  3166.          index = container.GetCount();
  3167.       if (index < 1 || index > container.GetCount())
  3168.          return;
  3169.       var nodeToDelete = container.RemoveElementAt(index, true);
  3170.       var nodeValue = nodeToDelete.QueryInterface(tmRDFResource).Value
  3171.       this.deleteSubtree(nodeValue);
  3172.       if (!container.GetCount()) this.deleteNode(rdfNodeTabs);
  3173.       this.saveStateDelayed();
  3174.    },
  3175.  
  3176.    loadOneTab: function SM_loadOneTab(rdfNodeSession, aTab) {
  3177.       aTab.setAttribute("inrestore", "true"); // flag. dont save tab that are in restore phase
  3178.       // load Properties before we load History
  3179.       var tabProperties = this.getLiteralValue(rdfNodeSession, "properties");
  3180.       if (tabProperties != "") SessionData.setTabProperties(aTab, tabProperties, true);
  3181.       var aBrowser = gBrowser.getBrowserForTab(aTab);
  3182.       var webNav = aBrowser.webNavigation;
  3183.       var savedHistory = this.loadTabHistory(rdfNodeSession, webNav.sessionHistory);
  3184.       if (savedHistory == null) {
  3185.          tmLog("loadOneTab() - tab at index " + aTab._tPos + " failed to load data from the saved session");
  3186.          gBrowser.removeTab(aTab);
  3187.          return;
  3188.       }
  3189.  
  3190.       aBrowser._sessionData = {
  3191.          node: rdfNodeSession
  3192.       };
  3193.  
  3194.       try {
  3195.          // if url is file and file don't exist it throws an exception, we don't need to reload loacl file
  3196.          var url = webNav.sessionHistory.getEntryAtIndex(savedHistory.index, false).URI.spec;
  3197.          var needToReload = SessionPref.getBoolPref("restore.reloadall") && url.indexOf("file:")!=0;
  3198.          if (needToReload) {
  3199.             window.setTimeout( function (browser) {
  3200.               browser.addEventListener('load', SessionManager.afterTabLoad, true);
  3201.               const nsIWebNavigation = Components.interfaces.nsIWebNavigation;
  3202.               var _webNav = browser.webNavigation;
  3203.               try {
  3204.                  var sh = _webNav.sessionHistory;
  3205.                  if (sh)
  3206.                      _webNav = sh.QueryInterface(nsIWebNavigation);
  3207.               } catch (e) { }
  3208.  
  3209.               try {
  3210.                  const flags = nsIWebNavigation.LOAD_FLAGS_BYPASS_PROXY | nsIWebNavigation.LOAD_FLAGS_BYPASS_CACHE;
  3211.                  _webNav.reload(flags);
  3212.               } catch (e) { }
  3213.             }, 0, aBrowser);
  3214.          }
  3215.          else
  3216.             aBrowser.addEventListener('load', SessionManager.afterTabLoad, true);
  3217.  
  3218.          webNav.gotoIndex(savedHistory.index);
  3219.       } catch (e) {tmLog("error in loadOneTab gotoIndex ? ")}
  3220.  
  3221.    }, // end of "loadOneTab : function(...............)"
  3222.  
  3223.    afterTabLoad: function SM_afterTabLoad(event) {
  3224.       var browser = this;
  3225.       browser.removeEventListener('load', SessionManager.afterTabLoad, true);
  3226.       var data = browser._sessionData;
  3227.       var tab = gBrowser.getTabForBrowser(browser);
  3228.       var tabExist = tab && tab.parentNode; // make sure tab was not removed
  3229.  
  3230.       // restore scroll position
  3231.       if (SessionPref.getBoolPref("save.scrollposition")) {
  3232.          var XYZ = SessionManager.getLiteralValue(data.node, "scroll", "0,0,1");
  3233.          if (XYZ != "0,0,1") {
  3234.             XYZ = XYZ.split(",");
  3235.             try {
  3236.                var sHistory = browser.webNavigation.sessionHistory;
  3237.                var curHistory = sHistory.getEntryAtIndex(sHistory.index, false);
  3238.                curHistory.QueryInterface(tmSHEntry).setScrollPosition(XYZ[0], XYZ[1]);
  3239.             } catch (ex) {TMP_ASSERT(ex, "loadOneTab error index " + sHistory.index); }
  3240.             if (tabExist)
  3241.                SessionManager.setScrollPosition(tab, browser, {href: null, _scrollX: XYZ[0], _scrollY: XYZ[1], zoom: XYZ[2] || 1}, 15);
  3242.          }
  3243.       }
  3244.       if (tabExist) {
  3245.         tab.removeAttribute("inrestore");
  3246.       }
  3247.  
  3248.       // call ensureTabIsVisible for the current tab
  3249.       gBrowser.mTabContainer.ensureTabIsVisible(gBrowser.mTabContainer.selectedIndex);
  3250.  
  3251.       // check if we restore all tabs
  3252.       if (--gBrowser.tabsToLoad == 0) {
  3253.          delete gBrowser.tabsToLoad;
  3254.          checkBeforeAndAfter(); // just in case (we do it also in setTabTitle
  3255.          if (SessionManager.enableBackup){
  3256.             var result = SessionManager.saveOneWindow(gSessionPath[0], "windowbackup");
  3257.             if (result > 0)
  3258.                SessionManager.saveStateDelayed(-1);
  3259.          }
  3260.          SessionManager.setLiteral(gRDFRoot + "/closedSession/thisSession", "status", "crash");
  3261.       }
  3262.  
  3263.       delete browser._sessionData;
  3264.    },
  3265.  
  3266.    setScrollPosition: function SM_setScrollPosition(aTab, aBrowser, aData, attempts) {
  3267.       var bContent = aBrowser.contentWindow;
  3268.       var docViewer;
  3269.       // we don't use zoom after we drop support for Firefox 2.0 , so null the value
  3270.       aData.zoom = null;
  3271.       if (!aTab.hasAttribute("busy")) {
  3272.          if (bContent.scrollX != aData._scrollX || bContent.scrollY != aData._scrollY)
  3273.             bContent.scrollTo(aData._scrollX, aData._scrollY);
  3274.       }
  3275.       if (attempts && ( bContent.scrollX != aData._scrollX || bContent.scrollY != aData._scrollY)) {
  3276.          window.setTimeout(SessionManager.setScrollPosition, 50, aTab, aBrowser, aData, --attempts);
  3277.          return;
  3278.       } else {
  3279.          // if we save this before timeout sometimes scroll is not ready yet
  3280.          if (SessionManager.enableBackup)
  3281.             SessionManager.setLiteral(SessionManager.getNodeForTab(aTab), "scroll", aData._scrollX + "," + aData._scrollY + "," + aData.zoom);
  3282.          // call by openLinkWithHistory
  3283.          if (aData.href)
  3284.             window.setTimeout( function(aBrowser, aURI) {
  3285.                aBrowser.loadURI(aURI, null, null);
  3286.             }, 0, aBrowser, aData.href);
  3287.       }
  3288.    },
  3289.  
  3290.    loadTabHistory: function(rdfNodeSession, sHistoryInternal) {
  3291.       var history = this.getLiteralValue(rdfNodeSession, "history");
  3292.       var tmpData = history.split("|-|");
  3293.       var sep = tmpData.shift(); // remove seperator from data
  3294.       var historyData = tmpData.join("|-|").split(sep);
  3295.       if (historyData.length < HSitems) {
  3296.          tmLog("error in loadTabHistory" + "\n" + "historyData.length " + historyData.length + "\n" + "historyData " + historyData + "\n" + "history " + history);
  3297.          return null; // if it les then 3 no data !!
  3298.       }
  3299.       if (typeof(sHistoryInternal) == "undefined")
  3300.          sHistoryInternal = Components.classes["@mozilla.org/browser/shistory;1"]
  3301.                                  .createInstance(tmSHistory);
  3302.       sHistoryInternal = sHistoryInternal.QueryInterface(Components.interfaces.nsISHistoryInternal);
  3303.       var sessionIndex = this.getIntValue(rdfNodeSession, "index");
  3304.       var historyCount = historyData.length/HSitems;
  3305.       if ( sessionIndex < 0 || sessionIndex >= historyCount ) sessionIndex = historyCount - 1;
  3306.       var index, historyEntry, entryTitle, uriStr, newURI, XY;
  3307.       for ( var i = 0; i < historyCount; i++ ){
  3308.          index = i * HSitems;
  3309.          if (!this.enableSaveHistory && sessionIndex != i) continue;
  3310.          historyEntry = Components.classes["@mozilla.org/browser/session-history-entry;1"]
  3311.                            .createInstance(tmSHEntry);
  3312.          entryTitle = unescape(historyData[index]);
  3313.          uriStr = historyData[index + 1];
  3314.          if (uriStr == "") uriStr = "about:blank";
  3315.          newURI = gIOService.newURI(uriStr, null, null);
  3316.          historyEntry.setTitle(entryTitle);
  3317.          historyEntry.setURI(newURI);
  3318.          historyEntry.saveLayoutStateFlag = true;
  3319. // xxx check if we can use this to reload tab not from Cache;
  3320. // is it good to reload ALL the history ???
  3321.          if (SessionPref.getBoolPref("save.scrollposition")) {
  3322.             if (historyData[index + 2] != "0,0") {
  3323.                XY = historyData[index + 2].split(",");
  3324.                historyEntry.setScrollPosition(XY[0], XY[1]); // XY is array [x,y]
  3325.             }
  3326.          }
  3327.          sHistoryInternal.addEntry(historyEntry, true);
  3328.       }
  3329.       if (!this.enableSaveHistory) sessionIndex = 0;
  3330.       return {history: sHistoryInternal, index: sessionIndex};
  3331.    },
  3332.  
  3333.   /**
  3334.    * Back up and archive sessions
  3335.    */
  3336.   archiveSessions: function SM_archiveSessions() {
  3337.     var lastBackup = this.getMostRecentBackup();
  3338.     // Backup Sessions if there aren't any backups or
  3339.     // they haven't been backed up in the last 24 hrs.
  3340.     const SESSIONS_ARCHIVE_INTERVAL = 86400 * 1000;
  3341.     if (!lastBackup ||
  3342.         Date.now() - lastBackup.lastModifiedTime > SESSIONS_ARCHIVE_INTERVAL) {
  3343.       var maxBackups = 7;
  3344.       // The maximum number of daily sessions backups to
  3345.       // keep in <profile>/sessionbackups. Special values:
  3346.       // -1: unlimited
  3347.       //  0: no backups created (and deletes all existing backups)
  3348.       // "extensions.tabmix.sessions.max_backups";
  3349.       try {
  3350.         maxBackups = SessionPref.getIntPref("max_backups");
  3351.       } catch(ex) {}
  3352.  
  3353.       this.archiveSessionsFile(maxBackups, false /* don't force */);
  3354.     }
  3355.   },
  3356.  
  3357.   getSessionsBackupDir: function SM_getSessionsBackupDir(aCretate) {
  3358.     var sessionsBackupDir = this.profileDir;
  3359.     sessionsBackupDir.append("sessionbackups");
  3360.     if (aCretate && !sessionsBackupDir.exists())
  3361.       sessionsBackupDir.create(Ci.nsIFile.DIRECTORY_TYPE, 0700);
  3362.     return sessionsBackupDir;
  3363.   },
  3364.  
  3365.   /**
  3366.    * Get the most recent backup file.
  3367.    * @returns nsIFile backup file
  3368.    */
  3369.   getMostRecentBackup: function SM_getMostRecentBackup() {
  3370.     var sessionsBackupDir = this.getSessionsBackupDir(false);
  3371.     if (!sessionsBackupDir.exists())
  3372.       return null;
  3373.  
  3374.     var backups = [];
  3375.     var entries = sessionsBackupDir.directoryEntries;
  3376.     while (entries.hasMoreElements()) {
  3377.       var entry = entries.getNext().QueryInterface(Ci.nsIFile);
  3378.       if (!entry.isHidden() && entry.leafName.match(/^tabmix_sessions-.+(rdf)?$/))
  3379.         backups.push(entry.leafName);
  3380.     }
  3381.  
  3382.     if (backups.length ==  0)
  3383.       return null;
  3384.  
  3385.     backups.sort();
  3386.     var filename = backups.pop();
  3387.  
  3388.     var backupFile = sessionsBackupDir.clone();
  3389.     backupFile.append(filename);
  3390.     return backupFile;
  3391.   },
  3392.  
  3393.   /**
  3394.    * ArchiveSessionsFile()
  3395.    *
  3396.    * Creates a dated backup once a day in <profile>/sessionbackups.
  3397.    *
  3398.    * @param int aNumberOfBackups - the maximum number of backups to keep
  3399.    *
  3400.    * @param bool aForceArchive - forces creating an archive even if one was
  3401.    *                             already created that day (overwrites)
  3402.    */
  3403.   archiveSessionsFile:
  3404.   function SM_archiveSessionsFile(aNumberOfBackups, aForceArchive) {
  3405.     var sessionsBackupDir = this.getSessionsBackupDir(true);
  3406.     if (!sessionsBackupDir.exists())
  3407.       return; // unable to create directory!
  3408.  
  3409.     // construct the new leafname
  3410.     // Use YYYY-MM-DD (ISO 8601) as it doesn't contain illegal characters
  3411.     // and makes the alphabetical order of multiple backup files more useful.
  3412.     var d = new Date();
  3413.     var date = [d.getFullYear(), '-', d.getMonth()<9 ? "0":"", d.getMonth()+1, '-', d.getDate()<10 ? "0":"", d.getDate()].join('');
  3414.     var backupFilename = "tabmix_sessions-" + date + ".rdf"
  3415.     var backupFile = null;
  3416.     if (!aForceArchive) {
  3417.       var backupFileNames = [];
  3418.       var backupFilenamePrefix = backupFilename.substr(0, backupFilename.indexOf("-"));
  3419.       var entries = sessionsBackupDir.directoryEntries;
  3420.       while (entries.hasMoreElements()) {
  3421.         var entry = entries.getNext().QueryInterface(Ci.nsIFile);
  3422.         var backupName = entry.leafName;
  3423.         if (backupName.substr(0, backupFilenamePrefix.length) == backupFilenamePrefix) {
  3424.           if (backupName == backupFilename)
  3425.             backupFile = entry;
  3426.           backupFileNames.push(backupName);
  3427.         }
  3428.       }
  3429.  
  3430.       var numberOfBackupsToDelete = 0;
  3431.       if (aNumberOfBackups > -1)
  3432.         numberOfBackupsToDelete = backupFileNames.length - aNumberOfBackups;
  3433.       if (numberOfBackupsToDelete > 0) {
  3434.         // If we don't have today's backup, remove one more so that
  3435.         // the total backups after this operation does not exceed the
  3436.         // number specified in the pref.
  3437.         if (!backupFile)
  3438.           numberOfBackupsToDelete++;
  3439.  
  3440.         backupFileNames.sort();
  3441.         while (numberOfBackupsToDelete--) {
  3442.           backupFile = sessionsBackupDir.clone();
  3443.           backupFile.append(backupFileNames[0]);
  3444.           backupFile.remove(false);
  3445.           backupFileNames.shift();
  3446.         }
  3447.       }
  3448.  
  3449.       // do nothing if we either have today's backup already
  3450.       // or the user has set the pref to zero.
  3451.       if (backupFile || aNumberOfBackups == 0)
  3452.         return;
  3453.     }
  3454.  
  3455.     backupFile = sessionsBackupDir.clone();
  3456.     backupFile.append(backupFilename);
  3457.  
  3458.     if (aForceArchive && backupFile.exists())
  3459.         backupFile.remove(false);
  3460.  
  3461.     if (!backupFile.exists()) {
  3462.       var sessionsFile = this.profileDir;
  3463.       sessionsFile.append("session.rdf");
  3464.       if (sessionsFile.exists())
  3465.          sessionsFile.copyTo(sessionsBackupDir, backupFilename);
  3466.     }
  3467.   }
  3468.  
  3469. };
  3470.